diff --git a/.gitignore b/.gitignore index 9b16e7ca24..dc2f0774d7 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ build .gradle *.apk app/manifest-merger-release-report.txt +*~* diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..c3b207f108 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "app/otr4j"] + path = app/otr4j + url = https://github.com/otr4j/otr4j.git diff --git a/LICENSE b/LICENSE index 855e555ac4..4e9382270b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,62 +1,94 @@ -Copyright (c) 2013, Redsolution LTD. +Copyright (c) 2015, Redsolution Inc. All rights reserved. -Project is distributed under "GNU General Public License, Version 3" (see assets/LICENSE-GPLv3.txt). +Project is distributed under GNU General Public License, Version 3 (see app/src/main/assets/LICENSE-GPLv3.txt). -Note about restrictions: +Following files as well as translations in app/src/main/res/values* are distributed under Creative Commons Attribution Non-Commercial Share Alike license: -Following files are distributed under "Creative Commons Attribution Non-Commercial No Derivatives": -res/drawable*/ic_connect.png -res/drawable*/ic_disconnect.png -res/drawable*/ic_information.png -res/drawable*/ic_launcher.png -res/drawable*/ic_stat_*.png -res/drawable*/title_logo.9.png +app/src/main/res/drawable/about_backdrop.jpg +app/src/main/res/drawable*/chat_background.png +app/src/main/res/drawable/contact_shadow.jpg +app/src/main/res/drawable*/drawer_*.jpg +app/src/main/res/drawable*/ic_avatar_*.png +app/src/main/res/drawable*/ic_client_*.png +app/src/main/res/drawable*/ic_offline_shadow_24dp.png +app/src/main/res/drawable*/ic_scroll_indicator_circle_*.png +app/src/main/res/drawable*/ic_stat_offline.png +app/src/main/res/drawable*/ic_stat_cnline.png +app/src/main/res/drawable*/ic_status_*.png +app/src/main/res/drawable*/message_*.9.png -Following files are part of The Android Open Source Project licensed under the Apache License, Version 2.0 (the "License") (see assets/LICENSE-Apache2.txt): -res/drawable/notify_panel_notification_icon_bg_tile.xml -res/layout/info_preference.xml -res/layout/preference.xml -res/layout/progress_preference.xml -res/drawable*/emo_im_*.png -res/drawable*/ic_menu_*.png +Following files are part of official icon set from Google and distributed under Creative Common Attribution 4.0 International License (CC-BY 4.0): -The rest of graphic in res/drawable* as well as translations in res/values* are distributed under "Creative Commons Attribution Non-Commercial Share Alike". +app/src/main/res/drawable*/ic_add_white_24dp.png +app/src/main/res/drawable*/ic_arrow_left_white_24dp.png +app/src/main/res/drawable*/ic_button_send_*_24dp.png +app/src/main/res/drawable*/ic_chat_white_24dp.png +app/src/main/res/drawable*/ic_clear_all_white_24dp.png +app/src/main/res/drawable*/ic_clear_white_24dp.png +app/src/main/res/drawable*/ic_exit_grey600_24dp.png +app/src/main/res/drawable*/ic_expander_indicator_down_black_24dp.png +app/src/main/res/drawable*/ic_expander_indicator_right_black_24dp.png +app/src/main/res/drawable*/ic_group_add_white_24dp.png +app/src/main/res/drawable*/ic_hangouts_grey600_24dp.png +app/src/main/res/drawable*/ic_help_circle_grey600_24dp.png +app/src/main/res/drawable*/ic_list_white_24dp.png +app/src/main/res/drawable*/ic_message_delivered_18dp.png +app/src/main/res/drawable*/ic_message_has_error_18dp.png +app/src/main/res/drawable*/ic_message_not_sent_18dp.png +app/src/main/res/drawable*/ic_person_add_white_24dp.png +app/src/main/res/drawable*/ic_search_white_24dp.png +app/src/main/res/drawable*/ic_security_*_24dp.png +app/src/main/res/drawable*/ic_settings_grey600_24dp.png +app/src/main/res/drawable*/ic_show_offline_*.png +app/src/main/res/drawable*/ic_stat_add_circle.png +app/src/main/res/drawable*/ic_stat_chat.png +app/src/main/res/drawable*/ic_stat_error.png +app/src/main/res/drawable*/ic_stat_help.png +app/src/main/res/drawable*/ic_stat_play_circle_fill.png +app/src/main/res/drawable*/ic_vcard_address_24dp.png +app/src/main/res/drawable*/ic_vcard_birthday_24dp.png +app/src/main/res/drawable*/ic_vcard_contact_info_24dp.png +app/src/main/res/drawable*/ic_vcard_email_24dp.png +app/src/main/res/drawable*/ic_vcard_job_title_24dp.png +app/src/main/res/drawable*/ic_vcard_notes_24dp.png +app/src/main/res/drawable*/ic_vcard_phone_24dp.png +app/src/main/res/drawable*/ic_vcard_web_24dp.png -Also following components are in use (see the source code or official sites for license details): +XMPP logo is licensed under a slightly modified MIT license (http://xmpp.org/about-xmpp/xsf/xsf-ipr-policy/#legal): +app/src/main/res/drawable*/ic_vcard_xmpp_24dp.png -ZXing -src/com/google/zxing/* -Apache License, Version 2.0 (the "License"). +The Jabber logo is licensed under the Creative Commons Attribution License +app/src/main/res/drawable*/ic_vcard_jabber_24dp.png + +Xabber, Redsolution logo are trademarks of Redsolution Inc., and are licensed under Creative Commons Attribution-NoDerivs license: +app/src/main/res/drawable*/ic_connect.png +app/src/main/res/drawable*/ic_disconnect.png +app/src/main/res/drawable*/ic_launcher.png +app/src/main/res/drawable*/redsolution_logo*.9.png -JZLib -src/com/jcraft/jzlib/* -BSD license. +Following file is part of The Android Open Source Project licensed under the Apache License, Version 2.0 (the "License") (see assets/LICENSE-Apache2.txt): +app/src/main/res/layout/preference.xml + +Also source code of following components is included in repository (see the source code or official sites for license details): Novel sasl client -src/com/novell/sasl/* +app/src/main/java/com/novell/sasl/* The OpenLDAP Public License. Measite Smack SASL -src/de/measite/smack/* +app/src/main/java/de/measite/smack/* Apache License, Version 2.0 (the "License"). Apache Harmony -src/org/apache/harmony/* +app/src/main/java/org/apache/harmony/* Apache License, Version 2.0 (the "License"). Apache Qpid -src/org/apache/qpid/* +app/src/main/java/org/apache/qpid/* Apache License, Version 2.0 (the "License"). Smack -src/org/jivesoftware/* +app/src/main/java/org/jivesoftware/* Apache License, Version 2.0 (the "License"). -DNS Java -src/org/xbill/* -BSD license. - -OTR4J -libs/otr4j.jar -GNU Lesser General Public License, Version 3. diff --git a/README.rst b/README.rst index 88e27359c6..3c3276e8e1 100644 --- a/README.rst +++ b/README.rst @@ -4,6 +4,19 @@ Xabber - XMPP client for Android Open source Jabber (XMPP) client with multi-account support, clean and simple interface. Being both free (as in freedom!) and ad-free, Xabber is designed to be the best Jabber client for Android. +Build instrustions +================== + +Xabber uses Gradle build system. The only specific thing is git submodule for otr4j library. To make it work use following commands: + + :: + + git submodule init + + git submodule update + +And otr4j would be cloned to your local repository. + Supported protocols =================== @@ -27,12 +40,19 @@ Supported protocols * XEP-0059: Result Set Management * XEP-0136: Message Archiving * XEP-0224: Attention +* XEP-0077: In-Band Registration Translations ============ -We use webtranslateit.com as our translation system. -All related resources are automatically generated from files got with webtranslateit.com. -If you want to update any translation please email us to info[at]xabber.com and specify all the translation languages you want to edit. -Then we'll send an invitation to our project on webtranslateit.com. -Please don't create pull requests with translation fixes as any changes will be overwritten with the next update from webtranslateit.com. + + +We use crowdin.com as our translation system. +All related resources are automatically generated from files got with crowdin.com. +If you want to update any translation go to Xabber page https://crowdin.com/project/xabber and request to join our translation team +Please don't create pull requests with translation fixes as any changes will be overwritten with the next update from crowdin.com. + +Wiki +==== + +Visit our wiki pages for additional information: https://github.com/redsolution/xabber-android/wiki diff --git a/app/build.gradle b/app/build.gradle index 1aff0155a4..b963ee9010 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,23 +1,55 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 9 - buildToolsVersion "21.1.2" + compileSdkVersion 22 + buildToolsVersion "22.0.1" defaultConfig { - applicationId "com.xabber.androiddev" - minSdkVersion 3 - targetSdkVersion 9 + minSdkVersion 14 + targetSdkVersion 22 + versionCode 200 + versionName '1.0.28' } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 + } + + lintOptions { + // problems with generated "applicationId" string resource + disable 'MissingTranslation' + } + + productFlavors { + + dev { + applicationId "com.xabber.androiddev" + resValue 'string', 'application_package', applicationId } + + beta { + applicationId "com.xabber.android.beta" + resValue 'string', 'application_package', applicationId + } + } } +repositories { + mavenCentral() +} + dependencies { - compile files('libs/otr4j.jar') + compile 'com.android.support:design:22.2.0' + compile 'com.android.support:appcompat-v7:22.2.0' + compile 'com.android.support:support-v13:22.2.0' + compile 'com.github.ksoichiro:android-observablescrollview:1.5.0' + compile 'de.hdodenhof:circleimageview:1.2.2' + compile 'com.melnykov:floatingactionbutton:1.2.0' + compile 'dnsjava:dnsjava:2.1.7' + compile 'com.github.bumptech.glide:glide:3.6.0' + compile 'com.google.zxing:android-integration:3.1.0' + compile 'com.jcraft:jzlib:1.0.7' + compile project('otr4j') } diff --git a/app/libs/otr4j.jar b/app/libs/otr4j.jar deleted file mode 100644 index 76a2b51ab5..0000000000 Binary files a/app/libs/otr4j.jar and /dev/null differ diff --git a/app/otr4j b/app/otr4j new file mode 160000 index 0000000000..0ced840950 --- /dev/null +++ b/app/otr4j @@ -0,0 +1 @@ +Subproject commit 0ced840950b4db1a403ea085b642a7028b1ef03d diff --git a/app/src/beta/res/values/application_title.xml b/app/src/beta/res/values/application_title.xml new file mode 100644 index 0000000000..2adc250af3 --- /dev/null +++ b/app/src/beta/res/values/application_title.xml @@ -0,0 +1,18 @@ + + + Xabber + Xabber Beta + Xabber + diff --git a/app/src/dev/res/values/application_title.xml b/app/src/dev/res/values/application_title.xml new file mode 100644 index 0000000000..47e7a80832 --- /dev/null +++ b/app/src/dev/res/values/application_title.xml @@ -0,0 +1,18 @@ + + + Xabber + Xabber Dev + Xabber + diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8f25d2e340..8f9babc534 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -13,20 +13,28 @@ along with this program. If not, see http://www.gnu.org/licenses/. --> + package="com.xabber.android"> + + + + + + + + + + + + + android:allowBackup="true" + android:icon="@drawable/ic_launcher" + android:label="@string/application_title_full" + android:theme="@style/Theme"> @@ -63,90 +71,155 @@ + android:parentActivityName="com.xabber.android.ui.preferences.PreferenceEditor" + android:theme="@style/ThemeDark" + > + + + + + android:parentActivityName="com.xabber.android.ui.preferences.PreferenceEditor" + > + + + - + android:parentActivityName="com.xabber.android.ui.preferences.AccountList" + > + + + + android:label="@string/occupant_list" + android:parentActivityName="com.xabber.android.ui.ChatViewer" + > + + + + + + + + + + android:name="com.xabber.android.ui.GroupEditor" + android:parentActivityName="com.xabber.android.ui.ContactViewer" + > + + + + android:label="@string/contact_add" + android:parentActivityName="com.xabber.android.ui.preferences.AccountList" + > + + + + android:label="@string/account_add" + android:parentActivityName="com.xabber.android.ui.preferences.AccountList" + > + + + + android:parentActivityName="com.xabber.android.ui.ContactList" + > + + + + android:label="@string/status_editor" + android:parentActivityName="com.xabber.android.ui.ContactList" + > + + + + android:parentActivityName="com.xabber.android.ui.ContactList" + > + + + + + + android:parentActivityName="com.xabber.android.ui.ChatViewer" + > + + + + + + + + @@ -155,19 +228,51 @@ + + + + + + + android:parentActivityName="com.xabber.android.ui.ChatViewer" + > + + + + + android:parentActivityName="com.xabber.android.ui.ChatViewer" + > + + + + + + + + + android:name="com.xabber.android.ui.preferences.PhraseList" + android:parentActivityName="com.xabber.android.ui.preferences.NotificationsSettings"> + + + + android:name="com.xabber.android.ui.preferences.PhraseEditor" + android:parentActivityName="com.xabber.android.ui.preferences.PhraseList"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + - - - - - - - - - - - - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/java/com/google/zxing/integration/android/IntentIntegrator.java b/app/src/main/java/com/google/zxing/integration/android/IntentIntegrator.java deleted file mode 100644 index cd4327784c..0000000000 --- a/app/src/main/java/com/google/zxing/integration/android/IntentIntegrator.java +++ /dev/null @@ -1,415 +0,0 @@ -/* - * Copyright 2009 ZXing authors - * - * 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 com.google.zxing.integration.android; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import android.app.Activity; -import android.app.AlertDialog; -import android.content.ActivityNotFoundException; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.net.Uri; -import android.os.Bundle; -import android.util.Log; - -/** - *

A utility class which helps ease integration with Barcode Scanner via {@link Intent}s. This is a simple - * way to invoke barcode scanning and receive the result, without any need to integrate, modify, or learn the - * project's source code.

- * - *

Initiating a barcode scan

- * - *

To integrate, create an instance of {@code IntentIntegrator} and call {@link #initiateScan()} and wait - * for the result in your app.

- * - *

It does require that the Barcode Scanner (or work-alike) application is installed. The - * {@link #initiateScan()} method will prompt the user to download the application, if needed.

- * - *

There are a few steps to using this integration. First, your {@link Activity} must implement - * the method {@link Activity#onActivityResult(int, int, Intent)} and include a line of code like this:

- * - *
{@code
- * public void onActivityResult(int requestCode, int resultCode, Intent intent) {
- *   IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
- *   if (scanResult != null) {
- *     // handle scan result
- *   }
- *   // else continue with any other code you need in the method
- *   ...
- * }
- * }
- * - *

This is where you will handle a scan result.

- * - *

Second, just call this in response to a user action somewhere to begin the scan process:

- * - *
{@code
- * IntentIntegrator integrator = new IntentIntegrator(yourActivity);
- * integrator.initiateScan();
- * }
- * - *

Note that {@link #initiateScan()} returns an {@link AlertDialog} which is non-null if the - * user was prompted to download the application. This lets the calling app potentially manage the dialog. - * In particular, ideally, the app dismisses the dialog if it's still active in its {@link Activity#onPause()} - * method.

- * - *

You can use {@link #setTitle(String)} to customize the title of this download prompt dialog (or, use - * {@link #setTitleByID(int)} to set the title by string resource ID.) Likewise, the prompt message, and - * yes/no button labels can be changed.

- * - *

Finally, you can use {@link #addExtra(String, Object)} to add more parameters to the Intent used - * to invoke the scanner. This can be used to set additional options not directly exposed by this - * simplified API.

- * - *

By default, this will only allow applications that are known to respond to this intent correctly - * do so. The apps that are allowed to response can be set with {@link #setTargetApplications(Collection)}. - * For example, set to {@link #TARGET_BARCODE_SCANNER_ONLY} to only target the Barcode Scanner app itself.

- * - *

Sharing text via barcode

- * - *

To share text, encoded as a QR Code on-screen, similarly, see {@link #shareText(CharSequence)}.

- * - *

Some code, particularly download integration, was contributed from the Anobiit application.

- * - *

Enabling experimental barcode formats

- * - *

Some formats are not enabled by default even when scanning with {@link #ALL_CODE_TYPES}, such as - * {@link com.google.zxing.BarcodeFormat#PDF_417}. Use {@link #initiateScan(java.util.Collection)} with - * a collection containing the names of formats to scan for explicitly, like "PDF_417", to use such - * formats.

- * - * @author Sean Owen - * @author Fred Lin - * @author Isaac Potoczny-Jones - * @author Brad Drehmer - * @author gcstang - */ -public class IntentIntegrator { - - public static final int REQUEST_CODE = 0x0000c0de; // Only use bottom 16 bits - private static final String TAG = IntentIntegrator.class.getSimpleName(); - - public static final String DEFAULT_TITLE = "Install Barcode Scanner?"; - public static final String DEFAULT_MESSAGE = - "This application requires Barcode Scanner. Would you like to install it?"; - public static final String DEFAULT_YES = "Yes"; - public static final String DEFAULT_NO = "No"; - - public static final String BS_PACKAGE = "com.google.zxing.client.android"; - private static final String BSPLUS_PACKAGE = "com.srowen.bs.android"; - - // supported barcode formats - public static final Collection PRODUCT_CODE_TYPES = list("UPC_A", "UPC_E", "EAN_8", "EAN_13", "RSS_14"); - public static final Collection ONE_D_CODE_TYPES = - list("UPC_A", "UPC_E", "EAN_8", "EAN_13", "CODE_39", "CODE_93", "CODE_128", - "ITF", "RSS_14", "RSS_EXPANDED"); - public static final Collection QR_CODE_TYPES = Collections.singleton("QR_CODE"); - public static final Collection DATA_MATRIX_TYPES = Collections.singleton("DATA_MATRIX"); - - public static final Collection ALL_CODE_TYPES = null; - - public static final Collection TARGET_BARCODE_SCANNER_ONLY = Collections.singleton(BS_PACKAGE); - public static final Collection TARGET_ALL_KNOWN = list( - BS_PACKAGE, // Barcode Scanner - BSPLUS_PACKAGE, // Barcode Scanner+ - BSPLUS_PACKAGE + ".simple" // Barcode Scanner+ Simple - // What else supports this intent? - ); - - private final Activity activity; - private String title; - private String message; - private String buttonYes; - private String buttonNo; - private Collection targetApplications; - private final Map moreExtras; - - public IntentIntegrator(Activity activity) { - this.activity = activity; - title = DEFAULT_TITLE; - message = DEFAULT_MESSAGE; - buttonYes = DEFAULT_YES; - buttonNo = DEFAULT_NO; - targetApplications = TARGET_ALL_KNOWN; - moreExtras = new HashMap(3); - } - - public String getTitle() { - return title; - } - - public void setTitle(String title) { - this.title = title; - } - - public void setTitleByID(int titleID) { - title = activity.getString(titleID); - } - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } - - public void setMessageByID(int messageID) { - message = activity.getString(messageID); - } - - public String getButtonYes() { - return buttonYes; - } - - public void setButtonYes(String buttonYes) { - this.buttonYes = buttonYes; - } - - public void setButtonYesByID(int buttonYesID) { - buttonYes = activity.getString(buttonYesID); - } - - public String getButtonNo() { - return buttonNo; - } - - public void setButtonNo(String buttonNo) { - this.buttonNo = buttonNo; - } - - public void setButtonNoByID(int buttonNoID) { - buttonNo = activity.getString(buttonNoID); - } - - public Collection getTargetApplications() { - return targetApplications; - } - - public void setTargetApplications(Collection targetApplications) { - this.targetApplications = targetApplications; - } - - public void setSingleTargetApplication(String targetApplication) { - this.targetApplications = Collections.singleton(targetApplication); - } - - public Map getMoreExtras() { - return moreExtras; - } - - public void addExtra(String key, Object value) { - moreExtras.put(key, value); - } - - /** - * Initiates a scan for all known barcode types. - */ - public AlertDialog initiateScan() { - return initiateScan(ALL_CODE_TYPES); - } - - /** - * Initiates a scan only for a certain set of barcode types, given as strings corresponding - * to their names in ZXing's {@code BarcodeFormat} class like "UPC_A". You can supply constants - * like {@link #PRODUCT_CODE_TYPES} for example. - * - * @return the {@link AlertDialog} that was shown to the user prompting them to download the app - * if a prompt was needed, or null otherwise - */ - public AlertDialog initiateScan(Collection desiredBarcodeFormats) { - Intent intentScan = new Intent(BS_PACKAGE + ".SCAN"); - intentScan.addCategory(Intent.CATEGORY_DEFAULT); - - // check which types of codes to scan for - if (desiredBarcodeFormats != null) { - // set the desired barcode types - StringBuilder joinedByComma = new StringBuilder(); - for (String format : desiredBarcodeFormats) { - if (joinedByComma.length() > 0) { - joinedByComma.append(','); - } - joinedByComma.append(format); - } - intentScan.putExtra("SCAN_FORMATS", joinedByComma.toString()); - } - - String targetAppPackage = findTargetAppPackage(intentScan); - if (targetAppPackage == null) { - return showDownloadDialog(); - } -// intentScan.setPackage(targetAppPackage); - intentScan.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - intentScan.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); - attachMoreExtras(intentScan); - startActivityForResult(intentScan, REQUEST_CODE); - return null; - } - - /** - * Start an activity.
- * This method is defined to allow different methods of activity starting for - * newer versions of Android and for compatibility library. - * - * @param intent Intent to start. - * @param code Request code for the activity - * @see android.app.Activity#startActivityForResult(Intent, int) - * @see android.app.Fragment#startActivityForResult(Intent, int) - */ - protected void startActivityForResult(Intent intent, int code) { - activity.startActivityForResult(intent, code); - } - - private String findTargetAppPackage(Intent intent) { - PackageManager pm = activity.getPackageManager(); - List availableApps = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); - if (availableApps != null) { - for (ResolveInfo availableApp : availableApps) { - String packageName = availableApp.activityInfo.packageName; - if (targetApplications.contains(packageName)) { - return packageName; - } - } - } - return null; - } - - private AlertDialog showDownloadDialog() { - AlertDialog.Builder downloadDialog = new AlertDialog.Builder(activity); - downloadDialog.setTitle(title); - downloadDialog.setMessage(message); - downloadDialog.setPositiveButton(buttonYes, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - Uri uri = Uri.parse("market://details?id=" + BS_PACKAGE); - Intent intent = new Intent(Intent.ACTION_VIEW, uri); - try { - activity.startActivity(intent); - } catch (ActivityNotFoundException anfe) { - // Hmm, market is not installed - Log.w(TAG, "Android Market is not installed; cannot install Barcode Scanner"); - } - } - }); - downloadDialog.setNegativeButton(buttonNo, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) {} - }); - return downloadDialog.show(); - } - - - /** - *

Call this from your {@link Activity}'s - * {@link Activity#onActivityResult(int, int, Intent)} method.

- * - * @return null if the event handled here was not related to this class, or - * else an {@link IntentResult} containing the result of the scan. If the user cancelled scanning, - * the fields will be null. - */ - public static IntentResult parseActivityResult(int requestCode, int resultCode, Intent intent) { - if (requestCode == REQUEST_CODE) { - if (resultCode == Activity.RESULT_OK) { - String contents = intent.getStringExtra("SCAN_RESULT"); - String formatName = intent.getStringExtra("SCAN_RESULT_FORMAT"); - byte[] rawBytes = intent.getByteArrayExtra("SCAN_RESULT_BYTES"); - int intentOrientation = intent.getIntExtra("SCAN_RESULT_ORIENTATION", Integer.MIN_VALUE); - Integer orientation = intentOrientation == Integer.MIN_VALUE ? null : intentOrientation; - String errorCorrectionLevel = intent.getStringExtra("SCAN_RESULT_ERROR_CORRECTION_LEVEL"); - return new IntentResult(contents, - formatName, - rawBytes, - orientation, - errorCorrectionLevel); - } - return new IntentResult(); - } - return null; - } - - - /** - * Defaults to type "TEXT_TYPE". - * @see #shareText(CharSequence, CharSequence) - */ - public AlertDialog shareText(CharSequence text) { - return shareText(text, "TEXT_TYPE"); - } - - /** - * Shares the given text by encoding it as a barcode, such that another user can - * scan the text off the screen of the device. - * - * @param text the text string to encode as a barcode - * @param type type of data to encode. See {@code com.google.zxing.client.android.Contents.Type} constants. - * @return the {@link AlertDialog} that was shown to the user prompting them to download the app - * if a prompt was needed, or null otherwise - */ - public AlertDialog shareText(CharSequence text, CharSequence type) { - Intent intent = new Intent(); - intent.addCategory(Intent.CATEGORY_DEFAULT); - intent.setAction(BS_PACKAGE + ".ENCODE"); - intent.putExtra("ENCODE_TYPE", type); - intent.putExtra("ENCODE_DATA", text); - String targetAppPackage = findTargetAppPackage(intent); - if (targetAppPackage == null) { - return showDownloadDialog(); - } -// intent.setPackage(targetAppPackage); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); - attachMoreExtras(intent); - activity.startActivity(intent); - return null; - } - - private static Collection list(String... values) { - return Collections.unmodifiableCollection(Arrays.asList(values)); - } - - private void attachMoreExtras(Intent intent) { - for (Map.Entry entry : moreExtras.entrySet()) { - String key = entry.getKey(); - Object value = entry.getValue(); - // Kind of hacky - if (value instanceof Integer) { - intent.putExtra(key, (Integer) value); - } else if (value instanceof Long) { - intent.putExtra(key, (Long) value); - } else if (value instanceof Boolean) { - intent.putExtra(key, (Boolean) value); - } else if (value instanceof Double) { - intent.putExtra(key, (Double) value); - } else if (value instanceof Float) { - intent.putExtra(key, (Float) value); - } else if (value instanceof Bundle) { - intent.putExtra(key, (Bundle) value); - } else { - intent.putExtra(key, value.toString()); - } - } - } - -} diff --git a/app/src/main/java/com/google/zxing/integration/android/IntentResult.java b/app/src/main/java/com/google/zxing/integration/android/IntentResult.java deleted file mode 100644 index 2469af92c0..0000000000 --- a/app/src/main/java/com/google/zxing/integration/android/IntentResult.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2009 ZXing authors - * - * 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 com.google.zxing.integration.android; - -/** - *

Encapsulates the result of a barcode scan invoked through {@link IntentIntegrator}.

- * - * @author Sean Owen - */ -public final class IntentResult { - - private final String contents; - private final String formatName; - private final byte[] rawBytes; - private final Integer orientation; - private final String errorCorrectionLevel; - - IntentResult() { - this(null, null, null, null, null); - } - - IntentResult(String contents, - String formatName, - byte[] rawBytes, - Integer orientation, - String errorCorrectionLevel) { - this.contents = contents; - this.formatName = formatName; - this.rawBytes = rawBytes; - this.orientation = orientation; - this.errorCorrectionLevel = errorCorrectionLevel; - } - - /** - * @return raw content of barcode - */ - public String getContents() { - return contents; - } - - /** - * @return name of format, like "QR_CODE", "UPC_A". See {@code BarcodeFormat} for more format names. - */ - public String getFormatName() { - return formatName; - } - - /** - * @return raw bytes of the barcode content, if applicable, or null otherwise - */ - public byte[] getRawBytes() { - return rawBytes; - } - - /** - * @return rotation of the image, in degrees, which resulted in a successful scan. May be null. - */ - public Integer getOrientation() { - return orientation; - } - - /** - * @return name of the error correction level used in the barcode, if applicable - */ - public String getErrorCorrectionLevel() { - return errorCorrectionLevel; - } - - @Override - public String toString() { - StringBuilder dialogText = new StringBuilder(100); - dialogText.append("Format: ").append(formatName).append('\n'); - dialogText.append("Contents: ").append(contents).append('\n'); - int rawBytesLength = rawBytes == null ? 0 : rawBytes.length; - dialogText.append("Raw bytes: (").append(rawBytesLength).append(" bytes)\n"); - dialogText.append("Orientation: ").append(orientation).append('\n'); - dialogText.append("EC level: ").append(errorCorrectionLevel).append('\n'); - return dialogText.toString(); - } - -} diff --git a/app/src/main/java/com/jcraft/jzlib/Adler32.java b/app/src/main/java/com/jcraft/jzlib/Adler32.java deleted file mode 100644 index d8b6ef8633..0000000000 --- a/app/src/main/java/com/jcraft/jzlib/Adler32.java +++ /dev/null @@ -1,94 +0,0 @@ -/* -*-mode:java; c-basic-offset:2; -*- */ -/* -Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - - 3. The names of the authors may not be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, -INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, -OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* - * This program is based on zlib-1.1.3, so all credit should go authors - * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) - * and contributors of zlib. - */ - -package com.jcraft.jzlib; - -final class Adler32{ - - // largest prime smaller than 65536 - static final private int BASE=65521; - // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 - static final private int NMAX=5552; - - long adler32(long adler, byte[] buf, int index, int len){ - if(buf == null){ return 1L; } - - long s1=adler&0xffff; - long s2=(adler>>16)&0xffff; - int k; - - while(len > 0) { - k=len=16){ - s1+=buf[index++]&0xff; s2+=s1; - s1+=buf[index++]&0xff; s2+=s1; - s1+=buf[index++]&0xff; s2+=s1; - s1+=buf[index++]&0xff; s2+=s1; - s1+=buf[index++]&0xff; s2+=s1; - s1+=buf[index++]&0xff; s2+=s1; - s1+=buf[index++]&0xff; s2+=s1; - s1+=buf[index++]&0xff; s2+=s1; - s1+=buf[index++]&0xff; s2+=s1; - s1+=buf[index++]&0xff; s2+=s1; - s1+=buf[index++]&0xff; s2+=s1; - s1+=buf[index++]&0xff; s2+=s1; - s1+=buf[index++]&0xff; s2+=s1; - s1+=buf[index++]&0xff; s2+=s1; - s1+=buf[index++]&0xff; s2+=s1; - s1+=buf[index++]&0xff; s2+=s1; - k-=16; - } - if(k!=0){ - do{ - s1+=buf[index++]&0xff; s2+=s1; - } - while(--k!=0); - } - s1%=BASE; - s2%=BASE; - } - return (s2<<16)|s1; - } - - /* - private java.util.zip.Adler32 adler=new java.util.zip.Adler32(); - long adler32(long value, byte[] buf, int index, int len){ - if(value==1) {adler.reset();} - if(buf==null) {adler.reset();} - else{adler.update(buf, index, len);} - return adler.getValue(); - } - */ -} diff --git a/app/src/main/java/com/jcraft/jzlib/ChangeLog b/app/src/main/java/com/jcraft/jzlib/ChangeLog deleted file mode 100644 index c828fae3ef..0000000000 --- a/app/src/main/java/com/jcraft/jzlib/ChangeLog +++ /dev/null @@ -1,115 +0,0 @@ -ChangeLog of JZlib -==================================================================== -Last modified: Thu Aug 18 16:16:06 UTC 2005 - - -Changes since version 1.0.6: -- change: memory and performance optimizations in the inflate operation. - Many thanks to Paul Wakefield at platx.org(http://www.platx.org), who - suggested above improvements. -- change: added the nowrap argument to Z{Input,Output}?Stream. - - -Changes since version 1.0.5: -- ZInputStream.read(byte[], int, int) method return sometimes zero - instead of -1 at the end of stream. -- ZOutputStream.end() method should not declare IOException. -- It should be possible to call ZOutputStream.end() method more times - (because you can call close() method and after it end() method in - finally block). -- ZOutputStream.finish() method should not ignore IOException from flush(). -Many thanks to Matej Kraus at seznam.cz , who pointed out above problems. - - -Changes since version 1.0.4: -- a minor bug fix in ZInputStream - - -Changes since version 1.0.3: -- fixed a bug in closing ZOutputStream. - The inflating and deflating processes were not finished successfully. -- added 'finish' and 'end' methods to ZOutputStream.java -- added 'example/test_stream_deflate_inflate.java' - - -Changes since version 1.0.2: -- enabled pure Java implementation of adler32 and - commented out the dependency on J2SE in Adler32.java for J2ME users. - - -Changes since version 1.0.1: -- fixed a bug in deflating some trivial data, for example, - large data chunk filled with zero. -- added 'version' method to JZlib class. - - -Changes since version 1.0.0: -- added ZInputStream and ZOutputStream classes. -- fixed a typo in LICENSE.txt. - - -Changes since version 0.0.9: -- fixed a bug in setting a dictionary in the inflation process. -- The license was changed to a BSD style license. -- Z{Input,Output}Stream classes were deleted. - - -Changes since version 0.0.8: -- fixed a bug in handling a preset dictionary in the inflation process. - - -Changes since version 0.0.7: -- added methods to control the window size (the size of the history - buffer) and to handle the no-wrap option (no zlib header or check), - which is the undocumented functionality of zlib. - - -Changes since version 0.0.6: -- updated InfBlocks.java as zlib did to fix the vulnerability related to - the 'double free'. Of course, JZlib is free from such a vulnerability - like the 'double free', but JZlib had crashed with NullPointerException - by a specially-crafted block of invalid deflated data. - - -Changes since version 0.0.5: -- added 'flush()' method to com.jcraft.jzlib.ZOutputStream. -- fixed to take care when occurring the buffer overflow in - com.jcraft.jzlib.ZOutputStream. -Many thanks to Tim Bendfelt at cs.wisc.edu , who pointed out above problems. - - -Changes since version 0.0.4: -............................ -- fixed a bug in Adler32 class. -- fixed a bug in ZInputStream class -- modified ZInputStream to be extended from InputStream - instead of FileInputStream. -- modified ZOutputStream to be extended from OutputStream - instead of FileOutputStream. -- modified ZStream to be changeable wbits. Give wbits value to - the method 'deflateInit' of ZStream. -Many thanks to Bryan Keller, who reported bugs -and made suggestions. - - -Changes since version 0.0.3: -............................ -- fixed a bug in the compression level 0. -- modified to be compatible with JIKES's bug. -- added Z[Input,Output]Stream written by Lapo Luchini. - - -Changes since version 0.0.2: -............................ -- fixed a critical bug, which had arisen in the deflating operation with - NO_FLUSH and DEFAULT_COMPRESSION mode. - Many thanks to Bryan Keller(keller@neomar.com), who reported this glitch. - - -Changes since version 0.0.1: -............................ -- fixed the bad compression ratio problem, which is reported from - Martin Smith(martin@spamcop.net). The compression ratio was not so good - compared with the compression ration of zlib. Now, this problem has been - fixed and, I hope, that deflated files by JZlib and zlib are completely - same bit by bit. diff --git a/app/src/main/java/com/jcraft/jzlib/Deflate.java b/app/src/main/java/com/jcraft/jzlib/Deflate.java deleted file mode 100644 index 99788020b0..0000000000 --- a/app/src/main/java/com/jcraft/jzlib/Deflate.java +++ /dev/null @@ -1,1623 +0,0 @@ -/* -*-mode:java; c-basic-offset:2; -*- */ -/* -Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - - 3. The names of the authors may not be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, -INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, -OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* - * This program is based on zlib-1.1.3, so all credit should go authors - * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) - * and contributors of zlib. - */ - -package com.jcraft.jzlib; - -public -final class Deflate{ - - static final private int MAX_MEM_LEVEL=9; - - static final private int Z_DEFAULT_COMPRESSION=-1; - - static final private int MAX_WBITS=15; // 32K LZ77 window - static final private int DEF_MEM_LEVEL=8; - - static class Config{ - int good_length; // reduce lazy search above this match length - int max_lazy; // do not perform lazy search above this match length - int nice_length; // quit search above this match length - int max_chain; - int func; - Config(int good_length, int max_lazy, - int nice_length, int max_chain, int func){ - this.good_length=good_length; - this.max_lazy=max_lazy; - this.nice_length=nice_length; - this.max_chain=max_chain; - this.func=func; - } - } - - static final private int STORED=0; - static final private int FAST=1; - static final private int SLOW=2; - static final private Config[] config_table; - static{ - config_table=new Config[10]; - // good lazy nice chain - config_table[0]=new Config(0, 0, 0, 0, STORED); - config_table[1]=new Config(4, 4, 8, 4, FAST); - config_table[2]=new Config(4, 5, 16, 8, FAST); - config_table[3]=new Config(4, 6, 32, 32, FAST); - - config_table[4]=new Config(4, 4, 16, 16, SLOW); - config_table[5]=new Config(8, 16, 32, 32, SLOW); - config_table[6]=new Config(8, 16, 128, 128, SLOW); - config_table[7]=new Config(8, 32, 128, 256, SLOW); - config_table[8]=new Config(32, 128, 258, 1024, SLOW); - config_table[9]=new Config(32, 258, 258, 4096, SLOW); - } - - static final private String[] z_errmsg = { - "need dictionary", // Z_NEED_DICT 2 - "stream end", // Z_STREAM_END 1 - "", // Z_OK 0 - "file error", // Z_ERRNO (-1) - "stream error", // Z_STREAM_ERROR (-2) - "data error", // Z_DATA_ERROR (-3) - "insufficient memory", // Z_MEM_ERROR (-4) - "buffer error", // Z_BUF_ERROR (-5) - "incompatible version",// Z_VERSION_ERROR (-6) - "" - }; - - // block not completed, need more input or more output - static final private int NeedMore=0; - - // block flush performed - static final private int BlockDone=1; - - // finish started, need only more output at next deflate - static final private int FinishStarted=2; - - // finish done, accept no more input or output - static final private int FinishDone=3; - - // preset dictionary flag in zlib header - static final private int PRESET_DICT=0x20; - - static final private int Z_FILTERED=1; - static final private int Z_HUFFMAN_ONLY=2; - static final private int Z_DEFAULT_STRATEGY=0; - - static final private int Z_NO_FLUSH=0; - static final private int Z_PARTIAL_FLUSH=1; - static final private int Z_SYNC_FLUSH=2; - static final private int Z_FULL_FLUSH=3; - static final private int Z_FINISH=4; - - static final private int Z_OK=0; - static final private int Z_STREAM_END=1; - static final private int Z_NEED_DICT=2; - static final private int Z_ERRNO=-1; - static final private int Z_STREAM_ERROR=-2; - static final private int Z_DATA_ERROR=-3; - static final private int Z_MEM_ERROR=-4; - static final private int Z_BUF_ERROR=-5; - static final private int Z_VERSION_ERROR=-6; - - static final private int INIT_STATE=42; - static final private int BUSY_STATE=113; - static final private int FINISH_STATE=666; - - // The deflate compression method - static final private int Z_DEFLATED=8; - - static final private int STORED_BLOCK=0; - static final private int STATIC_TREES=1; - static final private int DYN_TREES=2; - - // The three kinds of block type - static final private int Z_BINARY=0; - static final private int Z_ASCII=1; - static final private int Z_UNKNOWN=2; - - static final private int Buf_size=8*2; - - // repeat previous bit length 3-6 times (2 bits of repeat count) - static final private int REP_3_6=16; - - // repeat a zero length 3-10 times (3 bits of repeat count) - static final private int REPZ_3_10=17; - - // repeat a zero length 11-138 times (7 bits of repeat count) - static final private int REPZ_11_138=18; - - static final private int MIN_MATCH=3; - static final private int MAX_MATCH=258; - static final private int MIN_LOOKAHEAD=(MAX_MATCH+MIN_MATCH+1); - - static final private int MAX_BITS=15; - static final private int D_CODES=30; - static final private int BL_CODES=19; - static final private int LENGTH_CODES=29; - static final private int LITERALS=256; - static final private int L_CODES=(LITERALS+1+LENGTH_CODES); - static final private int HEAP_SIZE=(2*L_CODES+1); - - static final private int END_BLOCK=256; - - ZStream strm; // pointer back to this zlib stream - int status; // as the name implies - byte[] pending_buf; // output still pending - int pending_buf_size; // size of pending_buf - int pending_out; // next pending byte to output to the stream - int pending; // nb of bytes in the pending buffer - int noheader; // suppress zlib header and adler32 - byte data_type; // UNKNOWN, BINARY or ASCII - byte method; // STORED (for zip only) or DEFLATED - int last_flush; // value of flush param for previous deflate call - - int w_size; // LZ77 window size (32K by default) - int w_bits; // log2(w_size) (8..16) - int w_mask; // w_size - 1 - - byte[] window; - // Sliding window. Input bytes are read into the second half of the window, - // and move to the first half later to keep a dictionary of at least wSize - // bytes. With this organization, matches are limited to a distance of - // wSize-MAX_MATCH bytes, but this ensures that IO is always - // performed with a length multiple of the block size. Also, it limits - // the window size to 64K, which is quite useful on MSDOS. - // To do: use the user input buffer as sliding window. - - int window_size; - // Actual size of window: 2*wSize, except when the user input buffer - // is directly used as sliding window. - - short[] prev; - // Link to older string with same hash index. To limit the size of this - // array to 64K, this link is maintained only for the last 32K strings. - // An index in this array is thus a window index modulo 32K. - - short[] head; // Heads of the hash chains or NIL. - - int ins_h; // hash index of string to be inserted - int hash_size; // number of elements in hash table - int hash_bits; // log2(hash_size) - int hash_mask; // hash_size-1 - - // Number of bits by which ins_h must be shifted at each input - // step. It must be such that after MIN_MATCH steps, the oldest - // byte no longer takes part in the hash key, that is: - // hash_shift * MIN_MATCH >= hash_bits - int hash_shift; - - // Window position at the beginning of the current output block. Gets - // negative when the window is moved backwards. - - int block_start; - - int match_length; // length of best match - int prev_match; // previous match - int match_available; // set if previous match exists - int strstart; // start of string to insert - int match_start; // start of matching string - int lookahead; // number of valid bytes ahead in window - - // Length of the best match at previous step. Matches not greater than this - // are discarded. This is used in the lazy match evaluation. - int prev_length; - - // To speed up deflation, hash chains are never searched beyond this - // length. A higher limit improves compression ratio but degrades the speed. - int max_chain_length; - - // Attempt to find a better match only when the current match is strictly - // smaller than this value. This mechanism is used only for compression - // levels >= 4. - int max_lazy_match; - - // Insert new strings in the hash table only if the match length is not - // greater than this length. This saves time but degrades compression. - // max_insert_length is used only for compression levels <= 3. - - int level; // compression level (1..9) - int strategy; // favor or force Huffman coding - - // Use a faster search when the previous match is longer than this - int good_match; - - // Stop searching when current match exceeds this - int nice_match; - - short[] dyn_ltree; // literal and length tree - short[] dyn_dtree; // distance tree - short[] bl_tree; // Huffman tree for bit lengths - - Tree l_desc=new Tree(); // desc for literal tree - Tree d_desc=new Tree(); // desc for distance tree - Tree bl_desc=new Tree(); // desc for bit length tree - - // number of codes at each bit length for an optimal tree - short[] bl_count=new short[MAX_BITS+1]; - - // heap used to build the Huffman trees - int[] heap=new int[2*L_CODES+1]; - - int heap_len; // number of elements in the heap - int heap_max; // element of largest frequency - // The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. - // The same heap array is used to build all trees. - - // Depth of each subtree used as tie breaker for trees of equal frequency - byte[] depth=new byte[2*L_CODES+1]; - - int l_buf; // index for literals or lengths */ - - // Size of match buffer for literals/lengths. There are 4 reasons for - // limiting lit_bufsize to 64K: - // - frequencies can be kept in 16 bit counters - // - if compression is not successful for the first block, all input - // data is still in the window so we can still emit a stored block even - // when input comes from standard input. (This can also be done for - // all blocks if lit_bufsize is not greater than 32K.) - // - if compression is not successful for a file smaller than 64K, we can - // even emit a stored file instead of a stored block (saving 5 bytes). - // This is applicable only for zip (not gzip or zlib). - // - creating new Huffman trees less frequently may not provide fast - // adaptation to changes in the input data statistics. (Take for - // example a binary file with poorly compressible code followed by - // a highly compressible string table.) Smaller buffer sizes give - // fast adaptation but have of course the overhead of transmitting - // trees more frequently. - // - I can't count above 4 - int lit_bufsize; - - int last_lit; // running index in l_buf - - // Buffer for distances. To simplify the code, d_buf and l_buf have - // the same number of elements. To use different lengths, an extra flag - // array would be necessary. - - int d_buf; // index of pendig_buf - - int opt_len; // bit length of current block with optimal trees - int static_len; // bit length of current block with static trees - int matches; // number of string matches in current block - int last_eob_len; // bit length of EOB code for last block - - // Output buffer. bits are inserted starting at the bottom (least - // significant bits). - short bi_buf; - - // Number of valid bits in bi_buf. All bits above the last valid bit - // are always zero. - int bi_valid; - - Deflate(){ - dyn_ltree=new short[HEAP_SIZE*2]; - dyn_dtree=new short[(2*D_CODES+1)*2]; // distance tree - bl_tree=new short[(2*BL_CODES+1)*2]; // Huffman tree for bit lengths - } - - void lm_init() { - window_size=2*w_size; - - head[hash_size-1]=0; - for(int i=0; i= 3; max_blindex--) { - if (bl_tree[Tree.bl_order[max_blindex]*2+1] != 0) break; - } - // Update opt_len to include the bit length tree and counts - opt_len += 3*(max_blindex+1) + 5+5+4; - - return max_blindex; - } - - - // Send the header for a block using dynamic Huffman trees: the counts, the - // lengths of the bit length codes, the literal tree and the distance tree. - // IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. - void send_all_trees(int lcodes, int dcodes, int blcodes){ - int rank; // index in bl_order - - send_bits(lcodes-257, 5); // not +255 as stated in appnote.txt - send_bits(dcodes-1, 5); - send_bits(blcodes-4, 4); // not -3 as stated in appnote.txt - for (rank = 0; rank < blcodes; rank++) { - send_bits(bl_tree[Tree.bl_order[rank]*2+1], 3); - } - send_tree(dyn_ltree, lcodes-1); // literal tree - send_tree(dyn_dtree, dcodes-1); // distance tree - } - - // Send a literal or distance tree in compressed form, using the codes in - // bl_tree. - void send_tree (short[] tree,// the tree to be sent - int max_code // and its largest code of non zero frequency - ){ - int n; // iterates over all tree elements - int prevlen = -1; // last emitted length - int curlen; // length of current code - int nextlen = tree[0*2+1]; // length of next code - int count = 0; // repeat count of the current code - int max_count = 7; // max repeat count - int min_count = 4; // min repeat count - - if (nextlen == 0){ max_count = 138; min_count = 3; } - - for (n = 0; n <= max_code; n++) { - curlen = nextlen; nextlen = tree[(n+1)*2+1]; - if(++count < max_count && curlen == nextlen) { - continue; - } - else if(count < min_count) { - do { send_code(curlen, bl_tree); } while (--count != 0); - } - else if(curlen != 0){ - if(curlen != prevlen){ - send_code(curlen, bl_tree); count--; - } - send_code(REP_3_6, bl_tree); - send_bits(count-3, 2); - } - else if(count <= 10){ - send_code(REPZ_3_10, bl_tree); - send_bits(count-3, 3); - } - else{ - send_code(REPZ_11_138, bl_tree); - send_bits(count-11, 7); - } - count = 0; prevlen = curlen; - if(nextlen == 0){ - max_count = 138; min_count = 3; - } - else if(curlen == nextlen){ - max_count = 6; min_count = 3; - } - else{ - max_count = 7; min_count = 4; - } - } - } - - // Output a byte on the stream. - // IN assertion: there is enough room in pending_buf. - final void put_byte(byte[] p, int start, int len){ - System.arraycopy(p, start, pending_buf, pending, len); - pending+=len; - } - - final void put_byte(byte c){ - pending_buf[pending++]=c; - } - final void put_short(int w) { - put_byte((byte)(w/*&0xff*/)); - put_byte((byte)(w>>>8)); - } - final void putShortMSB(int b){ - put_byte((byte)(b>>8)); - put_byte((byte)(b/*&0xff*/)); - } - - final void send_code(int c, short[] tree){ - int c2=c*2; - send_bits((tree[c2]&0xffff), (tree[c2+1]&0xffff)); - } - - void send_bits(int value, int length){ - int len = length; - if (bi_valid > (int)Buf_size - len) { - int val = value; -// bi_buf |= (val << bi_valid); - bi_buf |= ((val << bi_valid)&0xffff); - put_short(bi_buf); - bi_buf = (short)(val >>> (Buf_size - bi_valid)); - bi_valid += len - Buf_size; - } else { -// bi_buf |= (value) << bi_valid; - bi_buf |= (((value) << bi_valid)&0xffff); - bi_valid += len; - } - } - - // Send one empty static block to give enough lookahead for inflate. - // This takes 10 bits, of which 7 may remain in the bit buffer. - // The current inflate code requires 9 bits of lookahead. If the - // last two codes for the previous block (real code plus EOB) were coded - // on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode - // the last real code. In this case we send two empty static blocks instead - // of one. (There are no problems if the previous block is stored or fixed.) - // To simplify the code, we assume the worst case of last real code encoded - // on one bit only. - void _tr_align(){ - send_bits(STATIC_TREES<<1, 3); - send_code(END_BLOCK, StaticTree.static_ltree); - - bi_flush(); - - // Of the 10 bits for the empty block, we have already sent - // (10 - bi_valid) bits. The lookahead for the last real code (before - // the EOB of the previous block) was thus at least one plus the length - // of the EOB plus what we have just sent of the empty static block. - if (1 + last_eob_len + 10 - bi_valid < 9) { - send_bits(STATIC_TREES<<1, 3); - send_code(END_BLOCK, StaticTree.static_ltree); - bi_flush(); - } - last_eob_len = 7; - } - - - // Save the match info and tally the frequency counts. Return true if - // the current block must be flushed. - boolean _tr_tally (int dist, // distance of matched string - int lc // match length-MIN_MATCH or unmatched char (if dist==0) - ){ - - pending_buf[d_buf+last_lit*2] = (byte)(dist>>>8); - pending_buf[d_buf+last_lit*2+1] = (byte)dist; - - pending_buf[l_buf+last_lit] = (byte)lc; last_lit++; - - if (dist == 0) { - // lc is the unmatched char - dyn_ltree[lc*2]++; - } - else { - matches++; - // Here, lc is the match length - MIN_MATCH - dist--; // dist = match distance - 1 - dyn_ltree[(Tree._length_code[lc]+LITERALS+1)*2]++; - dyn_dtree[Tree.d_code(dist)*2]++; - } - - if ((last_lit & 0x1fff) == 0 && level > 2) { - // Compute an upper bound for the compressed length - int out_length = last_lit*8; - int in_length = strstart - block_start; - int dcode; - for (dcode = 0; dcode < D_CODES; dcode++) { - out_length += (int)dyn_dtree[dcode*2] * - (5L+Tree.extra_dbits[dcode]); - } - out_length >>>= 3; - if ((matches < (last_lit/2)) && out_length < in_length/2) return true; - } - - return (last_lit == lit_bufsize-1); - // We avoid equality with lit_bufsize because of wraparound at 64K - // on 16 bit machines and because stored blocks are restricted to - // 64K-1 bytes. - } - - // Send the block data compressed using the given Huffman trees - void compress_block(short[] ltree, short[] dtree){ - int dist; // distance of matched string - int lc; // match length or unmatched char (if dist == 0) - int lx = 0; // running index in l_buf - int code; // the code to send - int extra; // number of extra bits to send - - if (last_lit != 0){ - do{ - dist=((pending_buf[d_buf+lx*2]<<8)&0xff00)| - (pending_buf[d_buf+lx*2+1]&0xff); - lc=(pending_buf[l_buf+lx])&0xff; lx++; - - if(dist == 0){ - send_code(lc, ltree); // send a literal byte - } - else{ - // Here, lc is the match length - MIN_MATCH - code = Tree._length_code[lc]; - - send_code(code+LITERALS+1, ltree); // send the length code - extra = Tree.extra_lbits[code]; - if(extra != 0){ - lc -= Tree.base_length[code]; - send_bits(lc, extra); // send the extra length bits - } - dist--; // dist is now the match distance - 1 - code = Tree.d_code(dist); - - send_code(code, dtree); // send the distance code - extra = Tree.extra_dbits[code]; - if (extra != 0) { - dist -= Tree.base_dist[code]; - send_bits(dist, extra); // send the extra distance bits - } - } // literal or match pair ? - - // Check that the overlay between pending_buf and d_buf+l_buf is ok: - } - while (lx < last_lit); - } - - send_code(END_BLOCK, ltree); - last_eob_len = ltree[END_BLOCK*2+1]; - } - - // Set the data type to ASCII or BINARY, using a crude approximation: - // binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. - // IN assertion: the fields freq of dyn_ltree are set and the total of all - // frequencies does not exceed 64K (to fit in an int on 16 bit machines). - void set_data_type(){ - int n = 0; - int ascii_freq = 0; - int bin_freq = 0; - while(n<7){ bin_freq += dyn_ltree[n*2]; n++;} - while(n<128){ ascii_freq += dyn_ltree[n*2]; n++;} - while(n (ascii_freq >>> 2) ? Z_BINARY : Z_ASCII); - } - - // Flush the bit buffer, keeping at most 7 bits in it. - void bi_flush(){ - if (bi_valid == 16) { - put_short(bi_buf); - bi_buf=0; - bi_valid=0; - } - else if (bi_valid >= 8) { - put_byte((byte)bi_buf); - bi_buf>>>=8; - bi_valid-=8; - } - } - - // Flush the bit buffer and align the output on a byte boundary - void bi_windup(){ - if (bi_valid > 8) { - put_short(bi_buf); - } else if (bi_valid > 0) { - put_byte((byte)bi_buf); - } - bi_buf = 0; - bi_valid = 0; - } - - // Copy a stored block, storing first the length and its - // one's complement if requested. - void copy_block(int buf, // the input data - int len, // its length - boolean header // true if block header must be written - ){ - int index=0; - bi_windup(); // align on byte boundary - last_eob_len = 8; // enough lookahead for inflate - - if (header) { - put_short((short)len); - put_short((short)~len); - } - - // while(len--!=0) { - // put_byte(window[buf+index]); - // index++; - // } - put_byte(window, buf, len); - } - - void flush_block_only(boolean eof){ - _tr_flush_block(block_start>=0 ? block_start : -1, - strstart-block_start, - eof); - block_start=strstart; - strm.flush_pending(); - } - - // Copy without compression as much as possible from the input stream, return - // the current block state. - // This function does not insert new strings in the dictionary since - // uncompressible data is probably not useful. This function is used - // only for the level=0 compression option. - // NOTE: this function should be optimized to avoid extra copying from - // window to pending_buf. - int deflate_stored(int flush){ - // Stored blocks are limited to 0xffff bytes, pending_buf is limited - // to pending_buf_size, and each stored block has a 5 byte header: - - int max_block_size = 0xffff; - int max_start; - - if(max_block_size > pending_buf_size - 5) { - max_block_size = pending_buf_size - 5; - } - - // Copy as much as possible from input to output: - while(true){ - // Fill the window as much as possible: - if(lookahead<=1){ - fill_window(); - if(lookahead==0 && flush==Z_NO_FLUSH) return NeedMore; - if(lookahead==0) break; // flush the current block - } - - strstart+=lookahead; - lookahead=0; - - // Emit a stored block if pending_buf will be full: - max_start=block_start+max_block_size; - if(strstart==0|| strstart>=max_start) { - // strstart == 0 is possible when wraparound on 16-bit machine - lookahead = (int)(strstart-max_start); - strstart = (int)max_start; - - flush_block_only(false); - if(strm.avail_out==0) return NeedMore; - - } - - // Flush if we may have to slide, otherwise block_start may become - // negative and the data will be gone: - if(strstart-block_start >= w_size-MIN_LOOKAHEAD) { - flush_block_only(false); - if(strm.avail_out==0) return NeedMore; - } - } - - flush_block_only(flush == Z_FINISH); - if(strm.avail_out==0) - return (flush == Z_FINISH) ? FinishStarted : NeedMore; - - return flush == Z_FINISH ? FinishDone : BlockDone; - } - - // Send a stored block - void _tr_stored_block(int buf, // input block - int stored_len, // length of input block - boolean eof // true if this is the last block for a file - ){ - send_bits((STORED_BLOCK<<1)+(eof?1:0), 3); // send block type - copy_block(buf, stored_len, true); // with header - } - - // Determine the best encoding for the current block: dynamic trees, static - // trees or store, and output the encoded block to the zip file. - void _tr_flush_block(int buf, // input block, or NULL if too old - int stored_len, // length of input block - boolean eof // true if this is the last block for a file - ) { - int opt_lenb, static_lenb;// opt_len and static_len in bytes - int max_blindex = 0; // index of last bit length code of non zero freq - - // Build the Huffman trees unless a stored block is forced - if(level > 0) { - // Check if the file is ascii or binary - if(data_type == Z_UNKNOWN) set_data_type(); - - // Construct the literal and distance trees - l_desc.build_tree(this); - - d_desc.build_tree(this); - - // At this point, opt_len and static_len are the total bit lengths of - // the compressed block data, excluding the tree representations. - - // Build the bit length tree for the above two trees, and get the index - // in bl_order of the last bit length code to send. - max_blindex=build_bl_tree(); - - // Determine the best encoding. Compute first the block length in bytes - opt_lenb=(opt_len+3+7)>>>3; - static_lenb=(static_len+3+7)>>>3; - - if(static_lenb<=opt_lenb) opt_lenb=static_lenb; - } - else { - opt_lenb=static_lenb=stored_len+5; // force a stored block - } - - if(stored_len+4<=opt_lenb && buf != -1){ - // 4: two words for the lengths - // The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. - // Otherwise we can't have processed more than WSIZE input bytes since - // the last block flush, because compression would have been - // successful. If LIT_BUFSIZE <= WSIZE, it is never too late to - // transform a block into a stored block. - _tr_stored_block(buf, stored_len, eof); - } - else if(static_lenb == opt_lenb){ - send_bits((STATIC_TREES<<1)+(eof?1:0), 3); - compress_block(StaticTree.static_ltree, StaticTree.static_dtree); - } - else{ - send_bits((DYN_TREES<<1)+(eof?1:0), 3); - send_all_trees(l_desc.max_code+1, d_desc.max_code+1, max_blindex+1); - compress_block(dyn_ltree, dyn_dtree); - } - - // The above check is made mod 2^32, for files larger than 512 MB - // and uLong implemented on 32 bits. - - init_block(); - - if(eof){ - bi_windup(); - } - } - - // Fill the window when the lookahead becomes insufficient. - // Updates strstart and lookahead. - // - // IN assertion: lookahead < MIN_LOOKAHEAD - // OUT assertions: strstart <= window_size-MIN_LOOKAHEAD - // At least one byte has been read, or avail_in == 0; reads are - // performed for at least two bytes (required for the zip translate_eol - // option -- not supported here). - void fill_window(){ - int n, m; - int p; - int more; // Amount of free space at the end of the window. - - do{ - more = (window_size-lookahead-strstart); - - // Deal with !@#$% 64K limit: - if(more==0 && strstart==0 && lookahead==0){ - more = w_size; - } - else if(more==-1) { - // Very unlikely, but possible on 16 bit machine if strstart == 0 - // and lookahead == 1 (input done one byte at time) - more--; - - // If the window is almost full and there is insufficient lookahead, - // move the upper half to the lower one to make room in the upper half. - } - else if(strstart >= w_size+ w_size-MIN_LOOKAHEAD) { - System.arraycopy(window, w_size, window, 0, w_size); - match_start-=w_size; - strstart-=w_size; // we now have strstart >= MAX_DIST - block_start-=w_size; - - // Slide the hash table (could be avoided with 32 bit values - // at the expense of memory usage). We slide even when level == 0 - // to keep the hash table consistent if we switch back to level > 0 - // later. (Using level 0 permanently is not an optimal usage of - // zlib, so we don't care about this pathological case.) - - n = hash_size; - p=n; - do { - m = (head[--p]&0xffff); - head[p]=(m>=w_size ? (short)(m-w_size) : 0); - } - while (--n != 0); - - n = w_size; - p = n; - do { - m = (prev[--p]&0xffff); - prev[p] = (m >= w_size ? (short)(m-w_size) : 0); - // If n is not on any hash chain, prev[n] is garbage but - // its value will never be used. - } - while (--n!=0); - more += w_size; - } - - if (strm.avail_in == 0) return; - - // If there was no sliding: - // strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && - // more == window_size - lookahead - strstart - // => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) - // => more >= window_size - 2*WSIZE + 2 - // In the BIG_MEM or MMAP case (not yet supported), - // window_size == input_size + MIN_LOOKAHEAD && - // strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. - // Otherwise, window_size == 2*WSIZE so more >= 2. - // If there was sliding, more >= WSIZE. So in all cases, more >= 2. - - n = strm.read_buf(window, strstart + lookahead, more); - lookahead += n; - - // Initialize the hash value now that we have some input: - if(lookahead >= MIN_MATCH) { - ins_h = window[strstart]&0xff; - ins_h=(((ins_h)<= MIN_MATCH){ - ins_h=(((ins_h)<=MIN_MATCH){ - // check_match(strstart, match_start, match_length); - - bflush=_tr_tally(strstart-match_start, match_length-MIN_MATCH); - - lookahead -= match_length; - - // Insert new strings in the hash table only if the match length - // is not too large. This saves time but degrades compression. - if(match_length <= max_lazy_match && - lookahead >= MIN_MATCH) { - match_length--; // string at strstart already in hash table - do{ - strstart++; - - ins_h=((ins_h<= MIN_MATCH) { - ins_h=(((ins_h)< 4096))) { - - // If prev_match is also MIN_MATCH, match_start is garbage - // but we will ignore the current match anyway. - match_length = MIN_MATCH-1; - } - } - - // If there was a match at the previous step and the current - // match is not better, output the previous match: - if(prev_length >= MIN_MATCH && match_length <= prev_length) { - int max_insert = strstart + lookahead - MIN_MATCH; - // Do not insert strings in hash table beyond this. - - // check_match(strstart-1, prev_match, prev_length); - - bflush=_tr_tally(strstart-1-prev_match, prev_length - MIN_MATCH); - - // Insert in hash table all strings up to the end of the match. - // strstart-1 and strstart are already inserted. If there is not - // enough lookahead, the last two strings are not inserted in - // the hash table. - lookahead -= prev_length-1; - prev_length -= 2; - do{ - if(++strstart <= max_insert) { - ins_h=(((ins_h)<(w_size-MIN_LOOKAHEAD) ? - strstart-(w_size-MIN_LOOKAHEAD) : 0; - int nice_match=this.nice_match; - - // Stop when cur_match becomes <= limit. To simplify the code, - // we prevent matches with the string of window index 0. - - int wmask = w_mask; - - int strend = strstart + MAX_MATCH; - byte scan_end1 = window[scan+best_len-1]; - byte scan_end = window[scan+best_len]; - - // The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. - // It is easy to get rid of this optimization if necessary. - - // Do not waste too much time if we already have a good match: - if (prev_length >= good_match) { - chain_length >>= 2; - } - - // Do not look for matches beyond the end of the input. This is necessary - // to make deflate deterministic. - if (nice_match > lookahead) nice_match = lookahead; - - do { - match = cur_match; - - // Skip to next match if the match length cannot increase - // or if the match length is less than 2: - if (window[match+best_len] != scan_end || - window[match+best_len-1] != scan_end1 || - window[match] != window[scan] || - window[++match] != window[scan+1]) continue; - - // The check at best_len-1 can be removed because it will be made - // again later. (This heuristic is not always a win.) - // It is not necessary to compare scan[2] and match[2] since they - // are always equal when the other bytes match, given that - // the hash keys are equal and that HASH_BITS >= 8. - scan += 2; match++; - - // We check for insufficient lookahead only every 8th comparison; - // the 256th check will be made at strstart+258. - do { - } while (window[++scan] == window[++match] && - window[++scan] == window[++match] && - window[++scan] == window[++match] && - window[++scan] == window[++match] && - window[++scan] == window[++match] && - window[++scan] == window[++match] && - window[++scan] == window[++match] && - window[++scan] == window[++match] && - scan < strend); - - len = MAX_MATCH - (int)(strend - scan); - scan = strend - MAX_MATCH; - - if(len>best_len) { - match_start = cur_match; - best_len = len; - if (len >= nice_match) break; - scan_end1 = window[scan+best_len-1]; - scan_end = window[scan+best_len]; - } - - } while ((cur_match = (prev[cur_match & wmask]&0xffff)) > limit - && --chain_length != 0); - - if (best_len <= lookahead) return best_len; - return lookahead; - } - - int deflateInit(ZStream strm, int level, int bits){ - return deflateInit2(strm, level, Z_DEFLATED, bits, DEF_MEM_LEVEL, - Z_DEFAULT_STRATEGY); - } - int deflateInit(ZStream strm, int level){ - return deflateInit(strm, level, MAX_WBITS); - } - int deflateInit2(ZStream strm, int level, int method, int windowBits, - int memLevel, int strategy){ - int noheader = 0; - // byte[] my_version=ZLIB_VERSION; - - // - // if (version == null || version[0] != my_version[0] - // || stream_size != sizeof(z_stream)) { - // return Z_VERSION_ERROR; - // } - - strm.msg = null; - - if (level == Z_DEFAULT_COMPRESSION) level = 6; - - if (windowBits < 0) { // undocumented feature: suppress zlib header - noheader = 1; - windowBits = -windowBits; - } - - if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || - method != Z_DEFLATED || - windowBits < 9 || windowBits > 15 || level < 0 || level > 9 || - strategy < 0 || strategy > Z_HUFFMAN_ONLY) { - return Z_STREAM_ERROR; - } - - strm.dstate = (Deflate)this; - - this.noheader = noheader; - w_bits = windowBits; - w_size = 1 << w_bits; - w_mask = w_size - 1; - - hash_bits = memLevel + 7; - hash_size = 1 << hash_bits; - hash_mask = hash_size - 1; - hash_shift = ((hash_bits+MIN_MATCH-1)/MIN_MATCH); - - window = new byte[w_size*2]; - prev = new short[w_size]; - head = new short[hash_size]; - - lit_bufsize = 1 << (memLevel + 6); // 16K elements by default - - // We overlay pending_buf and d_buf+l_buf. This works since the average - // output size for (length,distance) codes is <= 24 bits. - pending_buf = new byte[lit_bufsize*4]; - pending_buf_size = lit_bufsize*4; - - d_buf = lit_bufsize/2; - l_buf = (1+2)*lit_bufsize; - - this.level = level; - -//System.out.println("level="+level); - - this.strategy = strategy; - this.method = (byte)method; - - return deflateReset(strm); - } - - int deflateReset(ZStream strm){ - strm.total_in = strm.total_out = 0; - strm.msg = null; // - strm.data_type = Z_UNKNOWN; - - pending = 0; - pending_out = 0; - - if(noheader < 0) { - noheader = 0; // was set to -1 by deflate(..., Z_FINISH); - } - status = (noheader!=0) ? BUSY_STATE : INIT_STATE; - strm.adler=strm._adler.adler32(0, null, 0, 0); - - last_flush = Z_NO_FLUSH; - - tr_init(); - lm_init(); - return Z_OK; - } - - int deflateEnd(){ - if(status!=INIT_STATE && status!=BUSY_STATE && status!=FINISH_STATE){ - return Z_STREAM_ERROR; - } - // Deallocate in reverse order of allocations: - pending_buf=null; - head=null; - prev=null; - window=null; - // free - // dstate=null; - return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; - } - - int deflateParams(ZStream strm, int _level, int _strategy){ - int err=Z_OK; - - if(_level == Z_DEFAULT_COMPRESSION){ - _level = 6; - } - if(_level < 0 || _level > 9 || - _strategy < 0 || _strategy > Z_HUFFMAN_ONLY) { - return Z_STREAM_ERROR; - } - - if(config_table[level].func!=config_table[_level].func && - strm.total_in != 0) { - // Flush the last buffer: - err = strm.deflate(Z_PARTIAL_FLUSH); - } - - if(level != _level) { - level = _level; - max_lazy_match = config_table[level].max_lazy; - good_match = config_table[level].good_length; - nice_match = config_table[level].nice_length; - max_chain_length = config_table[level].max_chain; - } - strategy = _strategy; - return err; - } - - int deflateSetDictionary (ZStream strm, byte[] dictionary, int dictLength){ - int length = dictLength; - int index=0; - - if(dictionary == null || status != INIT_STATE) - return Z_STREAM_ERROR; - - strm.adler=strm._adler.adler32(strm.adler, dictionary, 0, dictLength); - - if(length < MIN_MATCH) return Z_OK; - if(length > w_size-MIN_LOOKAHEAD){ - length = w_size-MIN_LOOKAHEAD; - index=dictLength-length; // use the tail of the dictionary - } - System.arraycopy(dictionary, index, window, 0, length); - strstart = length; - block_start = length; - - // Insert all strings in the hash table (except for the last two bytes). - // s->lookahead stays null, so s->ins_h will be recomputed at the next - // call of fill_window. - - ins_h = window[0]&0xff; - ins_h=(((ins_h)<Z_FINISH || flush<0){ - return Z_STREAM_ERROR; - } - - if(strm.next_out == null || - (strm.next_in == null && strm.avail_in != 0) || - (status == FINISH_STATE && flush != Z_FINISH)) { - strm.msg=z_errmsg[Z_NEED_DICT-(Z_STREAM_ERROR)]; - return Z_STREAM_ERROR; - } - if(strm.avail_out == 0){ - strm.msg=z_errmsg[Z_NEED_DICT-(Z_BUF_ERROR)]; - return Z_BUF_ERROR; - } - - this.strm = strm; // just in case - old_flush = last_flush; - last_flush = flush; - - // Write the zlib header - if(status == INIT_STATE) { - int header = (Z_DEFLATED+((w_bits-8)<<4))<<8; - int level_flags=((level-1)&0xff)>>1; - - if(level_flags>3) level_flags=3; - header |= (level_flags<<6); - if(strstart!=0) header |= PRESET_DICT; - header+=31-(header % 31); - - status=BUSY_STATE; - putShortMSB(header); - - - // Save the adler32 of the preset dictionary: - if(strstart!=0){ - putShortMSB((int)(strm.adler>>>16)); - putShortMSB((int)(strm.adler&0xffff)); - } - strm.adler=strm._adler.adler32(0, null, 0, 0); - } - - // Flush as much pending output as possible - if(pending != 0) { - strm.flush_pending(); - if(strm.avail_out == 0) { - //System.out.println(" avail_out==0"); - // Since avail_out is 0, deflate will be called again with - // more output space, but possibly with both pending and - // avail_in equal to zero. There won't be anything to do, - // but this is not an error situation so make sure we - // return OK instead of BUF_ERROR at next call of deflate: - last_flush = -1; - return Z_OK; - } - - // Make sure there is something to do and avoid duplicate consecutive - // flushes. For repeated and useless calls with Z_FINISH, we keep - // returning Z_STREAM_END instead of Z_BUFF_ERROR. - } - else if(strm.avail_in==0 && flush <= old_flush && - flush != Z_FINISH) { - strm.msg=z_errmsg[Z_NEED_DICT-(Z_BUF_ERROR)]; - return Z_BUF_ERROR; - } - - // User must not provide more input after the first FINISH: - if(status == FINISH_STATE && strm.avail_in != 0) { - strm.msg=z_errmsg[Z_NEED_DICT-(Z_BUF_ERROR)]; - return Z_BUF_ERROR; - } - - // Start a new block or continue the current one. - if(strm.avail_in!=0 || lookahead!=0 || - (flush != Z_NO_FLUSH && status != FINISH_STATE)) { - int bstate=-1; - switch(config_table[level].func){ - case STORED: - bstate = deflate_stored(flush); - break; - case FAST: - bstate = deflate_fast(flush); - break; - case SLOW: - bstate = deflate_slow(flush); - break; - default: - } - - if (bstate==FinishStarted || bstate==FinishDone) { - status = FINISH_STATE; - } - if (bstate==NeedMore || bstate==FinishStarted) { - if(strm.avail_out == 0) { - last_flush = -1; // avoid BUF_ERROR next call, see above - } - return Z_OK; - // If flush != Z_NO_FLUSH && avail_out == 0, the next call - // of deflate should use the same flush parameter to make sure - // that the flush is complete. So we don't have to output an - // empty block here, this will be done at next call. This also - // ensures that for a very small output buffer, we emit at most - // one empty block. - } - - if (bstate==BlockDone) { - if(flush == Z_PARTIAL_FLUSH) { - _tr_align(); - } - else { // FULL_FLUSH or SYNC_FLUSH - _tr_stored_block(0, 0, false); - // For a full flush, this empty block will be recognized - // as a special marker by inflate_sync(). - if(flush == Z_FULL_FLUSH) { - //state.head[s.hash_size-1]=0; - for(int i=0; i>>16)); - putShortMSB((int)(strm.adler&0xffff)); - strm.flush_pending(); - - // If avail_out is zero, the application will call deflate again - // to flush the rest. - noheader = -1; // write the trailer only once! - return pending != 0 ? Z_OK : Z_STREAM_END; - } -} diff --git a/app/src/main/java/com/jcraft/jzlib/InfBlocks.java b/app/src/main/java/com/jcraft/jzlib/InfBlocks.java deleted file mode 100644 index f6997fc6bd..0000000000 --- a/app/src/main/java/com/jcraft/jzlib/InfBlocks.java +++ /dev/null @@ -1,614 +0,0 @@ -/* -*-mode:java; c-basic-offset:2; -*- */ -/* -Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - - 3. The names of the authors may not be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, -INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, -OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* - * This program is based on zlib-1.1.3, so all credit should go authors - * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) - * and contributors of zlib. - */ - -package com.jcraft.jzlib; - -final class InfBlocks{ - static final private int MANY=1440; - - // And'ing with mask[n] masks the lower n bits - static final private int[] inflate_mask = { - 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, - 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, - 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, - 0x00007fff, 0x0000ffff - }; - - // Table for deflate from PKZIP's appnote.txt. - static final int[] border = { // Order of the bit length code lengths - 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 - }; - - static final private int Z_OK=0; - static final private int Z_STREAM_END=1; - static final private int Z_NEED_DICT=2; - static final private int Z_ERRNO=-1; - static final private int Z_STREAM_ERROR=-2; - static final private int Z_DATA_ERROR=-3; - static final private int Z_MEM_ERROR=-4; - static final private int Z_BUF_ERROR=-5; - static final private int Z_VERSION_ERROR=-6; - - static final private int TYPE=0; // get type bits (3, including end bit) - static final private int LENS=1; // get lengths for stored - static final private int STORED=2;// processing stored block - static final private int TABLE=3; // get table lengths - static final private int BTREE=4; // get bit lengths tree for a dynamic block - static final private int DTREE=5; // get length, distance trees for a dynamic block - static final private int CODES=6; // processing fixed or dynamic block - static final private int DRY=7; // output remaining window bytes - static final private int DONE=8; // finished last block, done - static final private int BAD=9; // ot a data error--stuck here - - int mode; // current inflate_block mode - - int left; // if STORED, bytes left to copy - - int table; // table lengths (14 bits) - int index; // index into blens (or border) - int[] blens; // bit lengths of codes - int[] bb=new int[1]; // bit length tree depth - int[] tb=new int[1]; // bit length decoding tree - - InfCodes codes=new InfCodes(); // if CODES, current state - - int last; // true if this block is the last block - - // mode independent information - int bitk; // bits in bit buffer - int bitb; // bit buffer - int[] hufts; // single malloc for tree space - byte[] window; // sliding window - int end; // one byte after sliding window - int read; // window read pointer - int write; // window write pointer - Object checkfn; // check function - long check; // check on output - - InfTree inftree=new InfTree(); - - InfBlocks(ZStream z, Object checkfn, int w){ - hufts=new int[MANY*3]; - window=new byte[w]; - end=w; - this.checkfn = checkfn; - mode = TYPE; - reset(z, null); - } - - void reset(ZStream z, long[] c){ - if(c!=null) c[0]=check; - if(mode==BTREE || mode==DTREE){ - } - if(mode==CODES){ - codes.free(z); - } - mode=TYPE; - bitk=0; - bitb=0; - read=write=0; - - if(checkfn != null) - z.adler=check=z._adler.adler32(0L, null, 0, 0); - } - - int proc(ZStream z, int r){ - int t; // temporary storage - int b; // bit buffer - int k; // bits in bit buffer - int p; // input data pointer - int n; // bytes available there - int q; // output window write pointer - int m; // bytes to end of window or read pointer - - // copy input/output information to locals (UPDATE macro restores) - {p=z.next_in_index;n=z.avail_in;b=bitb;k=bitk;} - {q=write;m=(int)(q>> 1){ - case 0: // stored - {b>>>=(3);k-=(3);} - t = k & 7; // go to byte boundary - - {b>>>=(t);k-=(t);} - mode = LENS; // get length of stored block - break; - case 1: // fixed - { - int[] bl=new int[1]; - int[] bd=new int[1]; - int[][] tl=new int[1][]; - int[][] td=new int[1][]; - - InfTree.inflate_trees_fixed(bl, bd, tl, td, z); - codes.init(bl[0], bd[0], tl[0], 0, td[0], 0, z); - } - - {b>>>=(3);k-=(3);} - - mode = CODES; - break; - case 2: // dynamic - - {b>>>=(3);k-=(3);} - - mode = TABLE; - break; - case 3: // illegal - - {b>>>=(3);k-=(3);} - mode = BAD; - z.msg = "invalid block type"; - r = Z_DATA_ERROR; - - bitb=b; bitk=k; - z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; - write=q; - return inflate_flush(z,r); - } - break; - case LENS: - - while(k<(32)){ - if(n!=0){ - r=Z_OK; - } - else{ - bitb=b; bitk=k; - z.avail_in=n; - z.total_in+=p-z.next_in_index;z.next_in_index=p; - write=q; - return inflate_flush(z,r); - }; - n--; - b|=(z.next_in[p++]&0xff)<>> 16) & 0xffff) != (b & 0xffff)){ - mode = BAD; - z.msg = "invalid stored block lengths"; - r = Z_DATA_ERROR; - - bitb=b; bitk=k; - z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; - write=q; - return inflate_flush(z,r); - } - left = (b & 0xffff); - b = k = 0; // dump bits - mode = left!=0 ? STORED : (last!=0 ? DRY : TYPE); - break; - case STORED: - if (n == 0){ - bitb=b; bitk=k; - z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; - write=q; - return inflate_flush(z,r); - } - - if(m==0){ - if(q==end&&read!=0){ - q=0; m=(int)(qn) t = n; - if(t>m) t = m; - System.arraycopy(z.next_in, p, window, q, t); - p += t; n -= t; - q += t; m -= t; - if ((left -= t) != 0) - break; - mode = last!=0 ? DRY : TYPE; - break; - case TABLE: - - while(k<(14)){ - if(n!=0){ - r=Z_OK; - } - else{ - bitb=b; bitk=k; - z.avail_in=n; - z.total_in+=p-z.next_in_index;z.next_in_index=p; - write=q; - return inflate_flush(z,r); - }; - n--; - b|=(z.next_in[p++]&0xff)< 29 || ((t >> 5) & 0x1f) > 29) - { - mode = BAD; - z.msg = "too many length or distance symbols"; - r = Z_DATA_ERROR; - - bitb=b; bitk=k; - z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; - write=q; - return inflate_flush(z,r); - } - t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); - if(blens==null || blens.length>>=(14);k-=(14);} - - index = 0; - mode = BTREE; - case BTREE: - while (index < 4 + (table >>> 10)){ - while(k<(3)){ - if(n!=0){ - r=Z_OK; - } - else{ - bitb=b; bitk=k; - z.avail_in=n; - z.total_in+=p-z.next_in_index;z.next_in_index=p; - write=q; - return inflate_flush(z,r); - }; - n--; - b|=(z.next_in[p++]&0xff)<>>=(3);k-=(3);} - } - - while(index < 19){ - blens[border[index++]] = 0; - } - - bb[0] = 7; - t = inftree.inflate_trees_bits(blens, bb, tb, hufts, z); - if (t != Z_OK){ - r = t; - if (r == Z_DATA_ERROR){ - blens=null; - mode = BAD; - } - - bitb=b; bitk=k; - z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; - write=q; - return inflate_flush(z,r); - } - - index = 0; - mode = DTREE; - case DTREE: - while (true){ - t = table; - if(!(index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))){ - break; - } - - int[] h; - int i, j, c; - - t = bb[0]; - - while(k<(t)){ - if(n!=0){ - r=Z_OK; - } - else{ - bitb=b; bitk=k; - z.avail_in=n; - z.total_in+=p-z.next_in_index;z.next_in_index=p; - write=q; - return inflate_flush(z,r); - }; - n--; - b|=(z.next_in[p++]&0xff)<>>=(t);k-=(t); - blens[index++] = c; - } - else { // c == 16..18 - i = c == 18 ? 7 : c - 14; - j = c == 18 ? 11 : 3; - - while(k<(t+i)){ - if(n!=0){ - r=Z_OK; - } - else{ - bitb=b; bitk=k; - z.avail_in=n; - z.total_in+=p-z.next_in_index;z.next_in_index=p; - write=q; - return inflate_flush(z,r); - }; - n--; - b|=(z.next_in[p++]&0xff)<>>=(t);k-=(t); - - j += (b & inflate_mask[i]); - - b>>>=(i);k-=(i); - - i = index; - t = table; - if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || - (c == 16 && i < 1)){ - blens=null; - mode = BAD; - z.msg = "invalid bit length repeat"; - r = Z_DATA_ERROR; - - bitb=b; bitk=k; - z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; - write=q; - return inflate_flush(z,r); - } - - c = c == 16 ? blens[i-1] : 0; - do{ - blens[i++] = c; - } - while (--j!=0); - index = i; - } - } - - tb[0]=-1; - { - int[] bl=new int[1]; - int[] bd=new int[1]; - int[] tl=new int[1]; - int[] td=new int[1]; - bl[0] = 9; // must be <= 9 for lookahead assumptions - bd[0] = 6; // must be <= 9 for lookahead assumptions - - t = table; - t = inftree.inflate_trees_dynamic(257 + (t & 0x1f), - 1 + ((t >> 5) & 0x1f), - blens, bl, bd, tl, td, hufts, z); - - if (t != Z_OK){ - if (t == Z_DATA_ERROR){ - blens=null; - mode = BAD; - } - r = t; - - bitb=b; bitk=k; - z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; - write=q; - return inflate_flush(z,r); - } - codes.init(bl[0], bd[0], hufts, tl[0], hufts, td[0], z); - } - mode = CODES; - case CODES: - bitb=b; bitk=k; - z.avail_in=n; z.total_in+=p-z.next_in_index;z.next_in_index=p; - write=q; - - if ((r = codes.proc(this, z, r)) != Z_STREAM_END){ - return inflate_flush(z, r); - } - r = Z_OK; - codes.free(z); - - p=z.next_in_index; n=z.avail_in;b=bitb;k=bitk; - q=write;m=(int)(q z.avail_out) n = z.avail_out; - if (n!=0 && r == Z_BUF_ERROR) r = Z_OK; - - // update counters - z.avail_out -= n; - z.total_out += n; - - // update check information - if(checkfn != null) - z.adler=check=z._adler.adler32(check, window, q, n); - - // copy as far as end of window - System.arraycopy(window, q, z.next_out, p, n); - p += n; - q += n; - - // see if more to copy at beginning of window - if (q == end){ - // wrap pointers - q = 0; - if (write == end) - write = 0; - - // compute bytes to copy - n = write - q; - if (n > z.avail_out) n = z.avail_out; - if (n!=0 && r == Z_BUF_ERROR) r = Z_OK; - - // update counters - z.avail_out -= n; - z.total_out += n; - - // update check information - if(checkfn != null) - z.adler=check=z._adler.adler32(check, window, q, n); - - // copy - System.arraycopy(window, q, z.next_out, p, n); - p += n; - q += n; - } - - // update pointers - z.next_out_index = p; - read = q; - - // done - return r; - } -} diff --git a/app/src/main/java/com/jcraft/jzlib/InfCodes.java b/app/src/main/java/com/jcraft/jzlib/InfCodes.java deleted file mode 100644 index c768fb1cbe..0000000000 --- a/app/src/main/java/com/jcraft/jzlib/InfCodes.java +++ /dev/null @@ -1,605 +0,0 @@ -/* -*-mode:java; c-basic-offset:2; -*- */ -/* -Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - - 3. The names of the authors may not be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, -INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, -OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* - * This program is based on zlib-1.1.3, so all credit should go authors - * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) - * and contributors of zlib. - */ - -package com.jcraft.jzlib; - -final class InfCodes{ - - static final private int[] inflate_mask = { - 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, - 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, - 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, - 0x00007fff, 0x0000ffff - }; - - static final private int Z_OK=0; - static final private int Z_STREAM_END=1; - static final private int Z_NEED_DICT=2; - static final private int Z_ERRNO=-1; - static final private int Z_STREAM_ERROR=-2; - static final private int Z_DATA_ERROR=-3; - static final private int Z_MEM_ERROR=-4; - static final private int Z_BUF_ERROR=-5; - static final private int Z_VERSION_ERROR=-6; - - // waiting for "i:"=input, - // "o:"=output, - // "x:"=nothing - static final private int START=0; // x: set up for LEN - static final private int LEN=1; // i: get length/literal/eob next - static final private int LENEXT=2; // i: getting length extra (have base) - static final private int DIST=3; // i: get distance next - static final private int DISTEXT=4;// i: getting distance extra - static final private int COPY=5; // o: copying bytes in window, waiting for space - static final private int LIT=6; // o: got literal, waiting for output space - static final private int WASH=7; // o: got eob, possibly still output waiting - static final private int END=8; // x: got eob and all data flushed - static final private int BADCODE=9;// x: got error - - int mode; // current inflate_codes mode - - // mode dependent information - int len; - - int[] tree; // pointer into tree - int tree_index=0; - int need; // bits needed - - int lit; - - // if EXT or COPY, where and how much - int get; // bits to get for extra - int dist; // distance back to copy from - - byte lbits; // ltree bits decoded per branch - byte dbits; // dtree bits decoder per branch - int[] ltree; // literal/length/eob tree - int ltree_index; // literal/length/eob tree - int[] dtree; // distance tree - int dtree_index; // distance tree - - InfCodes(){ - } - void init(int bl, int bd, - int[] tl, int tl_index, - int[] td, int td_index, ZStream z){ - mode=START; - lbits=(byte)bl; - dbits=(byte)bd; - ltree=tl; - ltree_index=tl_index; - dtree = td; - dtree_index=td_index; - tree=null; - } - - int proc(InfBlocks s, ZStream z, int r){ - int j; // temporary storage - int[] t; // temporary pointer - int tindex; // temporary pointer - int e; // extra bits or operation - int b=0; // bit buffer - int k=0; // bits in bit buffer - int p=0; // input data pointer - int n; // bytes available there - int q; // output window write pointer - int m; // bytes to end of window or read pointer - int f; // pointer to copy strings from - - // copy input/output information to locals (UPDATE macro restores) - p=z.next_in_index;n=z.avail_in;b=s.bitb;k=s.bitk; - q=s.write;m=q= 258 && n >= 10){ - - s.bitb=b;s.bitk=k; - z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; - s.write=q; - r = inflate_fast(lbits, dbits, - ltree, ltree_index, - dtree, dtree_index, - s, z); - - p=z.next_in_index;n=z.avail_in;b=s.bitb;k=s.bitk; - q=s.write;m=q>>=(tree[tindex+1]); - k-=(tree[tindex+1]); - - e=tree[tindex]; - - if(e == 0){ // literal - lit = tree[tindex+2]; - mode = LIT; - break; - } - if((e & 16)!=0 ){ // length - get = e & 15; - len = tree[tindex+2]; - mode = LENEXT; - break; - } - if ((e & 64) == 0){ // next table - need = e; - tree_index = tindex/3+tree[tindex+2]; - break; - } - if ((e & 32)!=0){ // end of block - mode = WASH; - break; - } - mode = BADCODE; // invalid code - z.msg = "invalid literal/length code"; - r = Z_DATA_ERROR; - - s.bitb=b;s.bitk=k; - z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; - s.write=q; - return s.inflate_flush(z,r); - - case LENEXT: // i: getting length extra (have base) - j = get; - - while(k<(j)){ - if(n!=0)r=Z_OK; - else{ - - s.bitb=b;s.bitk=k; - z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; - s.write=q; - return s.inflate_flush(z,r); - } - n--; b|=(z.next_in[p++]&0xff)<>=j; - k-=j; - - need = dbits; - tree = dtree; - tree_index=dtree_index; - mode = DIST; - case DIST: // i: get distance next - j = need; - - while(k<(j)){ - if(n!=0)r=Z_OK; - else{ - - s.bitb=b;s.bitk=k; - z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; - s.write=q; - return s.inflate_flush(z,r); - } - n--; b|=(z.next_in[p++]&0xff)<>=tree[tindex+1]; - k-=tree[tindex+1]; - - e = (tree[tindex]); - if((e & 16)!=0){ // distance - get = e & 15; - dist = tree[tindex+2]; - mode = DISTEXT; - break; - } - if ((e & 64) == 0){ // next table - need = e; - tree_index = tindex/3 + tree[tindex+2]; - break; - } - mode = BADCODE; // invalid code - z.msg = "invalid distance code"; - r = Z_DATA_ERROR; - - s.bitb=b;s.bitk=k; - z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; - s.write=q; - return s.inflate_flush(z,r); - - case DISTEXT: // i: getting distance extra - j = get; - - while(k<(j)){ - if(n!=0)r=Z_OK; - else{ - - s.bitb=b;s.bitk=k; - z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; - s.write=q; - return s.inflate_flush(z,r); - } - n--; b|=(z.next_in[p++]&0xff)<>=j; - k-=j; - - mode = COPY; - case COPY: // o: copying bytes in window, waiting for space - f = q - dist; - while(f < 0){ // modulo window size-"while" instead - f += s.end; // of "if" handles invalid distances - } - while (len!=0){ - - if(m==0){ - if(q==s.end&&s.read!=0){q=0;m=q 7){ // return unused byte, if any - k -= 8; - n++; - p--; // can always return one - } - - s.write=q; r=s.inflate_flush(z,r); - q=s.write;m=q= 258 && n >= 10 - // get literal/length code - while(k<(20)){ // max bits for literal/length code - n--; - b|=(z.next_in[p++]&0xff)<>=(tp[tp_index_t_3+1]); k-=(tp[tp_index_t_3+1]); - - s.window[q++] = (byte)tp[tp_index_t_3+2]; - m--; - continue; - } - do { - - b>>=(tp[tp_index_t_3+1]); k-=(tp[tp_index_t_3+1]); - - if((e&16)!=0){ - e &= 15; - c = tp[tp_index_t_3+2] + ((int)b & inflate_mask[e]); - - b>>=e; k-=e; - - // decode distance base of block to copy - while(k<(15)){ // max bits for distance code - n--; - b|=(z.next_in[p++]&0xff)<>=(tp[tp_index_t_3+1]); k-=(tp[tp_index_t_3+1]); - - if((e&16)!=0){ - // get extra bits to add to distance base - e &= 15; - while(k<(e)){ // get extra bits (up to 13) - n--; - b|=(z.next_in[p++]&0xff)<>=(e); k-=(e); - - // do the copy - m -= c; - if (q >= d){ // offset before dest - // just copy - r=q-d; - if(q-r>0 && 2>(q-r)){ - s.window[q++]=s.window[r++]; // minimum count is three, - s.window[q++]=s.window[r++]; // so unroll loop a little - c-=2; - } - else{ - System.arraycopy(s.window, r, s.window, q, 2); - q+=2; r+=2; c-=2; - } - } - else{ // else offset after destination - r=q-d; - do{ - r+=s.end; // force pointer in window - }while(r<0); // covers invalid distances - e=s.end-r; - if(c>e){ // if source crosses, - c-=e; // wrapped copy - if(q-r>0 && e>(q-r)){ - do{s.window[q++] = s.window[r++];} - while(--e!=0); - } - else{ - System.arraycopy(s.window, r, s.window, q, e); - q+=e; r+=e; e=0; - } - r = 0; // copy rest from start of window - } - - } - - // copy all or what's left - if(q-r>0 && c>(q-r)){ - do{s.window[q++] = s.window[r++];} - while(--c!=0); - } - else{ - System.arraycopy(s.window, r, s.window, q, c); - q+=c; r+=c; c=0; - } - break; - } - else if((e&64)==0){ - t+=tp[tp_index_t_3+2]; - t+=(b&inflate_mask[e]); - tp_index_t_3=(tp_index+t)*3; - e=tp[tp_index_t_3]; - } - else{ - z.msg = "invalid distance code"; - - c=z.avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3; - - s.bitb=b;s.bitk=k; - z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; - s.write=q; - - return Z_DATA_ERROR; - } - } - while(true); - break; - } - - if((e&64)==0){ - t+=tp[tp_index_t_3+2]; - t+=(b&inflate_mask[e]); - tp_index_t_3=(tp_index+t)*3; - if((e=tp[tp_index_t_3])==0){ - - b>>=(tp[tp_index_t_3+1]); k-=(tp[tp_index_t_3+1]); - - s.window[q++]=(byte)tp[tp_index_t_3+2]; - m--; - break; - } - } - else if((e&32)!=0){ - - c=z.avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3; - - s.bitb=b;s.bitk=k; - z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; - s.write=q; - - return Z_STREAM_END; - } - else{ - z.msg="invalid literal/length code"; - - c=z.avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3; - - s.bitb=b;s.bitk=k; - z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; - s.write=q; - - return Z_DATA_ERROR; - } - } - while(true); - } - while(m>=258 && n>= 10); - - // not enough input or output--restore pointers and return - c=z.avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3; - - s.bitb=b;s.bitk=k; - z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; - s.write=q; - - return Z_OK; - } -} diff --git a/app/src/main/java/com/jcraft/jzlib/InfTree.java b/app/src/main/java/com/jcraft/jzlib/InfTree.java deleted file mode 100644 index cbca43667a..0000000000 --- a/app/src/main/java/com/jcraft/jzlib/InfTree.java +++ /dev/null @@ -1,520 +0,0 @@ -/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ -/* -Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - - 3. The names of the authors may not be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, -INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, -OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* - * This program is based on zlib-1.1.3, so all credit should go authors - * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) - * and contributors of zlib. - */ - -package com.jcraft.jzlib; - -final class InfTree{ - - static final private int MANY=1440; - - static final private int Z_OK=0; - static final private int Z_STREAM_END=1; - static final private int Z_NEED_DICT=2; - static final private int Z_ERRNO=-1; - static final private int Z_STREAM_ERROR=-2; - static final private int Z_DATA_ERROR=-3; - static final private int Z_MEM_ERROR=-4; - static final private int Z_BUF_ERROR=-5; - static final private int Z_VERSION_ERROR=-6; - - static final int fixed_bl = 9; - static final int fixed_bd = 5; - - static final int[] fixed_tl = { - 96,7,256, 0,8,80, 0,8,16, 84,8,115, - 82,7,31, 0,8,112, 0,8,48, 0,9,192, - 80,7,10, 0,8,96, 0,8,32, 0,9,160, - 0,8,0, 0,8,128, 0,8,64, 0,9,224, - 80,7,6, 0,8,88, 0,8,24, 0,9,144, - 83,7,59, 0,8,120, 0,8,56, 0,9,208, - 81,7,17, 0,8,104, 0,8,40, 0,9,176, - 0,8,8, 0,8,136, 0,8,72, 0,9,240, - 80,7,4, 0,8,84, 0,8,20, 85,8,227, - 83,7,43, 0,8,116, 0,8,52, 0,9,200, - 81,7,13, 0,8,100, 0,8,36, 0,9,168, - 0,8,4, 0,8,132, 0,8,68, 0,9,232, - 80,7,8, 0,8,92, 0,8,28, 0,9,152, - 84,7,83, 0,8,124, 0,8,60, 0,9,216, - 82,7,23, 0,8,108, 0,8,44, 0,9,184, - 0,8,12, 0,8,140, 0,8,76, 0,9,248, - 80,7,3, 0,8,82, 0,8,18, 85,8,163, - 83,7,35, 0,8,114, 0,8,50, 0,9,196, - 81,7,11, 0,8,98, 0,8,34, 0,9,164, - 0,8,2, 0,8,130, 0,8,66, 0,9,228, - 80,7,7, 0,8,90, 0,8,26, 0,9,148, - 84,7,67, 0,8,122, 0,8,58, 0,9,212, - 82,7,19, 0,8,106, 0,8,42, 0,9,180, - 0,8,10, 0,8,138, 0,8,74, 0,9,244, - 80,7,5, 0,8,86, 0,8,22, 192,8,0, - 83,7,51, 0,8,118, 0,8,54, 0,9,204, - 81,7,15, 0,8,102, 0,8,38, 0,9,172, - 0,8,6, 0,8,134, 0,8,70, 0,9,236, - 80,7,9, 0,8,94, 0,8,30, 0,9,156, - 84,7,99, 0,8,126, 0,8,62, 0,9,220, - 82,7,27, 0,8,110, 0,8,46, 0,9,188, - 0,8,14, 0,8,142, 0,8,78, 0,9,252, - 96,7,256, 0,8,81, 0,8,17, 85,8,131, - 82,7,31, 0,8,113, 0,8,49, 0,9,194, - 80,7,10, 0,8,97, 0,8,33, 0,9,162, - 0,8,1, 0,8,129, 0,8,65, 0,9,226, - 80,7,6, 0,8,89, 0,8,25, 0,9,146, - 83,7,59, 0,8,121, 0,8,57, 0,9,210, - 81,7,17, 0,8,105, 0,8,41, 0,9,178, - 0,8,9, 0,8,137, 0,8,73, 0,9,242, - 80,7,4, 0,8,85, 0,8,21, 80,8,258, - 83,7,43, 0,8,117, 0,8,53, 0,9,202, - 81,7,13, 0,8,101, 0,8,37, 0,9,170, - 0,8,5, 0,8,133, 0,8,69, 0,9,234, - 80,7,8, 0,8,93, 0,8,29, 0,9,154, - 84,7,83, 0,8,125, 0,8,61, 0,9,218, - 82,7,23, 0,8,109, 0,8,45, 0,9,186, - 0,8,13, 0,8,141, 0,8,77, 0,9,250, - 80,7,3, 0,8,83, 0,8,19, 85,8,195, - 83,7,35, 0,8,115, 0,8,51, 0,9,198, - 81,7,11, 0,8,99, 0,8,35, 0,9,166, - 0,8,3, 0,8,131, 0,8,67, 0,9,230, - 80,7,7, 0,8,91, 0,8,27, 0,9,150, - 84,7,67, 0,8,123, 0,8,59, 0,9,214, - 82,7,19, 0,8,107, 0,8,43, 0,9,182, - 0,8,11, 0,8,139, 0,8,75, 0,9,246, - 80,7,5, 0,8,87, 0,8,23, 192,8,0, - 83,7,51, 0,8,119, 0,8,55, 0,9,206, - 81,7,15, 0,8,103, 0,8,39, 0,9,174, - 0,8,7, 0,8,135, 0,8,71, 0,9,238, - 80,7,9, 0,8,95, 0,8,31, 0,9,158, - 84,7,99, 0,8,127, 0,8,63, 0,9,222, - 82,7,27, 0,8,111, 0,8,47, 0,9,190, - 0,8,15, 0,8,143, 0,8,79, 0,9,254, - 96,7,256, 0,8,80, 0,8,16, 84,8,115, - 82,7,31, 0,8,112, 0,8,48, 0,9,193, - - 80,7,10, 0,8,96, 0,8,32, 0,9,161, - 0,8,0, 0,8,128, 0,8,64, 0,9,225, - 80,7,6, 0,8,88, 0,8,24, 0,9,145, - 83,7,59, 0,8,120, 0,8,56, 0,9,209, - 81,7,17, 0,8,104, 0,8,40, 0,9,177, - 0,8,8, 0,8,136, 0,8,72, 0,9,241, - 80,7,4, 0,8,84, 0,8,20, 85,8,227, - 83,7,43, 0,8,116, 0,8,52, 0,9,201, - 81,7,13, 0,8,100, 0,8,36, 0,9,169, - 0,8,4, 0,8,132, 0,8,68, 0,9,233, - 80,7,8, 0,8,92, 0,8,28, 0,9,153, - 84,7,83, 0,8,124, 0,8,60, 0,9,217, - 82,7,23, 0,8,108, 0,8,44, 0,9,185, - 0,8,12, 0,8,140, 0,8,76, 0,9,249, - 80,7,3, 0,8,82, 0,8,18, 85,8,163, - 83,7,35, 0,8,114, 0,8,50, 0,9,197, - 81,7,11, 0,8,98, 0,8,34, 0,9,165, - 0,8,2, 0,8,130, 0,8,66, 0,9,229, - 80,7,7, 0,8,90, 0,8,26, 0,9,149, - 84,7,67, 0,8,122, 0,8,58, 0,9,213, - 82,7,19, 0,8,106, 0,8,42, 0,9,181, - 0,8,10, 0,8,138, 0,8,74, 0,9,245, - 80,7,5, 0,8,86, 0,8,22, 192,8,0, - 83,7,51, 0,8,118, 0,8,54, 0,9,205, - 81,7,15, 0,8,102, 0,8,38, 0,9,173, - 0,8,6, 0,8,134, 0,8,70, 0,9,237, - 80,7,9, 0,8,94, 0,8,30, 0,9,157, - 84,7,99, 0,8,126, 0,8,62, 0,9,221, - 82,7,27, 0,8,110, 0,8,46, 0,9,189, - 0,8,14, 0,8,142, 0,8,78, 0,9,253, - 96,7,256, 0,8,81, 0,8,17, 85,8,131, - 82,7,31, 0,8,113, 0,8,49, 0,9,195, - 80,7,10, 0,8,97, 0,8,33, 0,9,163, - 0,8,1, 0,8,129, 0,8,65, 0,9,227, - 80,7,6, 0,8,89, 0,8,25, 0,9,147, - 83,7,59, 0,8,121, 0,8,57, 0,9,211, - 81,7,17, 0,8,105, 0,8,41, 0,9,179, - 0,8,9, 0,8,137, 0,8,73, 0,9,243, - 80,7,4, 0,8,85, 0,8,21, 80,8,258, - 83,7,43, 0,8,117, 0,8,53, 0,9,203, - 81,7,13, 0,8,101, 0,8,37, 0,9,171, - 0,8,5, 0,8,133, 0,8,69, 0,9,235, - 80,7,8, 0,8,93, 0,8,29, 0,9,155, - 84,7,83, 0,8,125, 0,8,61, 0,9,219, - 82,7,23, 0,8,109, 0,8,45, 0,9,187, - 0,8,13, 0,8,141, 0,8,77, 0,9,251, - 80,7,3, 0,8,83, 0,8,19, 85,8,195, - 83,7,35, 0,8,115, 0,8,51, 0,9,199, - 81,7,11, 0,8,99, 0,8,35, 0,9,167, - 0,8,3, 0,8,131, 0,8,67, 0,9,231, - 80,7,7, 0,8,91, 0,8,27, 0,9,151, - 84,7,67, 0,8,123, 0,8,59, 0,9,215, - 82,7,19, 0,8,107, 0,8,43, 0,9,183, - 0,8,11, 0,8,139, 0,8,75, 0,9,247, - 80,7,5, 0,8,87, 0,8,23, 192,8,0, - 83,7,51, 0,8,119, 0,8,55, 0,9,207, - 81,7,15, 0,8,103, 0,8,39, 0,9,175, - 0,8,7, 0,8,135, 0,8,71, 0,9,239, - 80,7,9, 0,8,95, 0,8,31, 0,9,159, - 84,7,99, 0,8,127, 0,8,63, 0,9,223, - 82,7,27, 0,8,111, 0,8,47, 0,9,191, - 0,8,15, 0,8,143, 0,8,79, 0,9,255 - }; - static final int[] fixed_td = { - 80,5,1, 87,5,257, 83,5,17, 91,5,4097, - 81,5,5, 89,5,1025, 85,5,65, 93,5,16385, - 80,5,3, 88,5,513, 84,5,33, 92,5,8193, - 82,5,9, 90,5,2049, 86,5,129, 192,5,24577, - 80,5,2, 87,5,385, 83,5,25, 91,5,6145, - 81,5,7, 89,5,1537, 85,5,97, 93,5,24577, - 80,5,4, 88,5,769, 84,5,49, 92,5,12289, - 82,5,13, 90,5,3073, 86,5,193, 192,5,24577 - }; - - // Tables for deflate from PKZIP's appnote.txt. - static final int[] cplens = { // Copy lengths for literal codes 257..285 - 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, - 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 - }; - - // see note #13 above about 258 - static final int[] cplext = { // Extra bits for literal codes 257..285 - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, - 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112 // 112==invalid - }; - - static final int[] cpdist = { // Copy offsets for distance codes 0..29 - 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, - 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, - 8193, 12289, 16385, 24577 - }; - - static final int[] cpdext = { // Extra bits for distance codes - 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, - 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, - 12, 12, 13, 13}; - - // If BMAX needs to be larger than 16, then h and x[] should be uLong. - static final int BMAX=15; // maximum bit length of any code - - int[] hn = null; // hufts used in space - int[] v = null; // work area for huft_build - int[] c = null; // bit length count table - int[] r = null; // table entry for structure assignment - int[] u = null; // table stack - int[] x = null; // bit offsets, then code stack - - private int huft_build(int[] b, // code lengths in bits (all assumed <= BMAX) - int bindex, - int n, // number of codes (assumed <= 288) - int s, // number of simple-valued codes (0..s-1) - int[] d, // list of base values for non-simple codes - int[] e, // list of extra bits for non-simple codes - int[] t, // result: starting table - int[] m, // maximum lookup bits, returns actual - int[] hp,// space for trees - int[] hn,// hufts used in space - int[] v // working area: values in order of bit length - ){ - // Given a list of code lengths and a maximum table size, make a set of - // tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR - // if the given code set is incomplete (the tables are still built in this - // case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of - // lengths), or Z_MEM_ERROR if not enough memory. - - int a; // counter for codes of length k - int f; // i repeats in table every f entries - int g; // maximum code length - int h; // table level - int i; // counter, current code - int j; // counter - int k; // number of bits in current code - int l; // bits per table (returned in m) - int mask; // (1 << w) - 1, to avoid cc -O bug on HP - int p; // pointer into c[], b[], or v[] - int q; // points to current table - int w; // bits before this table == (l * h) - int xp; // pointer into x - int y; // number of dummy codes added - int z; // number of entries in current table - - // Generate counts for each bit length - - p = 0; i = n; - do { - c[b[bindex+p]]++; p++; i--; // assume all entries <= BMAX - }while(i!=0); - - if(c[0] == n){ // null input--all zero length codes - t[0] = -1; - m[0] = 0; - return Z_OK; - } - - // Find minimum and maximum length, bound *m by those - l = m[0]; - for (j = 1; j <= BMAX; j++) - if(c[j]!=0) break; - k = j; // minimum code length - if(l < j){ - l = j; - } - for (i = BMAX; i!=0; i--){ - if(c[i]!=0) break; - } - g = i; // maximum code length - if(l > i){ - l = i; - } - m[0] = l; - - // Adjust last length count to fill out codes, if needed - for (y = 1 << j; j < i; j++, y <<= 1){ - if ((y -= c[j]) < 0){ - return Z_DATA_ERROR; - } - } - if ((y -= c[i]) < 0){ - return Z_DATA_ERROR; - } - c[i] += y; - - // Generate starting offsets into the value table for each length - x[1] = j = 0; - p = 1; xp = 2; - while (--i!=0) { // note that i == g from above - x[xp] = (j += c[p]); - xp++; - p++; - } - - // Make a table of values in order of bit lengths - i = 0; p = 0; - do { - if ((j = b[bindex+p]) != 0){ - v[x[j]++] = i; - } - p++; - } - while (++i < n); - n = x[g]; // set n to length of v - - // Generate the Huffman codes and for each, make the table entries - x[0] = i = 0; // first Huffman code is zero - p = 0; // grab values in bit order - h = -1; // no tables yet--level -1 - w = -l; // bits decoded == (l * h) - u[0] = 0; // just to keep compilers happy - q = 0; // ditto - z = 0; // ditto - - // go through the bit lengths (k already is bits in shortest code) - for (; k <= g; k++){ - a = c[k]; - while (a--!=0){ - // here i is the Huffman code of length k bits for value *p - // make tables up to required level - while (k > w + l){ - h++; - w += l; // previous table always l bits - // compute minimum size table less than or equal to l bits - z = g - w; - z = (z > l) ? l : z; // table size upper limit - if((f=1<<(j=k-w))>a+1){ // try a k-w bit table - // too few codes for k-w bit table - f -= a + 1; // deduct codes from patterns left - xp = k; - if(j < z){ - while (++j < z){ // try smaller tables up to z bits - if((f <<= 1) <= c[++xp]) - break; // enough codes to use up j bits - f -= c[xp]; // else deduct codes from patterns - } - } - } - z = 1 << j; // table entries for j-bit table - - // allocate new table - if (hn[0] + z > MANY){ // (note: doesn't matter for fixed) - return Z_DATA_ERROR; // overflow of MANY - } - u[h] = q = /*hp+*/ hn[0]; // DEBUG - hn[0] += z; - - // connect to last table, if there is one - if(h!=0){ - x[h]=i; // save pattern for backing up - r[0]=(byte)j; // bits in this table - r[1]=(byte)l; // bits to dump before this table - j=i>>>(w - l); - r[2] = (int)(q - u[h-1] - j); // offset to this table - System.arraycopy(r, 0, hp, (u[h-1]+j)*3, 3); // connect to last table - } - else{ - t[0] = q; // first table is returned result - } - } - - // set up table entry in r - r[1] = (byte)(k - w); - if (p >= n){ - r[0] = 128 + 64; // out of values--invalid code - } - else if (v[p] < s){ - r[0] = (byte)(v[p] < 256 ? 0 : 32 + 64); // 256 is end-of-block - r[2] = v[p++]; // simple code is just the value - } - else{ - r[0]=(byte)(e[v[p]-s]+16+64); // non-simple--look up in lists - r[2]=d[v[p++] - s]; - } - - // fill code-like entries with r - f=1<<(k-w); - for (j=i>>>w;j>>= 1){ - i ^= j; - } - i ^= j; - - // backup over finished tables - mask = (1 << w) - 1; // needed on HP, cc -O bug - while ((i & mask) != x[h]){ - h--; // don't need to update q - w -= l; - mask = (1 << w) - 1; - } - } - } - // Return Z_BUF_ERROR if we were given an incomplete table - return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; - } - - int inflate_trees_bits(int[] c, // 19 code lengths - int[] bb, // bits tree desired/actual depth - int[] tb, // bits tree result - int[] hp, // space for trees - ZStream z // for messages - ){ - int result; - initWorkArea(19); - hn[0]=0; - result = huft_build(c, 0, 19, 19, null, null, tb, bb, hp, hn, v); - - if(result == Z_DATA_ERROR){ - z.msg = "oversubscribed dynamic bit lengths tree"; - } - else if(result == Z_BUF_ERROR || bb[0] == 0){ - z.msg = "incomplete dynamic bit lengths tree"; - result = Z_DATA_ERROR; - } - return result; - } - - int inflate_trees_dynamic(int nl, // number of literal/length codes - int nd, // number of distance codes - int[] c, // that many (total) code lengths - int[] bl, // literal desired/actual bit depth - int[] bd, // distance desired/actual bit depth - int[] tl, // literal/length tree result - int[] td, // distance tree result - int[] hp, // space for trees - ZStream z // for messages - ){ - int result; - - // build literal/length tree - initWorkArea(288); - hn[0]=0; - result = huft_build(c, 0, nl, 257, cplens, cplext, tl, bl, hp, hn, v); - if (result != Z_OK || bl[0] == 0){ - if(result == Z_DATA_ERROR){ - z.msg = "oversubscribed literal/length tree"; - } - else if (result != Z_MEM_ERROR){ - z.msg = "incomplete literal/length tree"; - result = Z_DATA_ERROR; - } - return result; - } - - // build distance tree - initWorkArea(288); - result = huft_build(c, nl, nd, 0, cpdist, cpdext, td, bd, hp, hn, v); - - if (result != Z_OK || (bd[0] == 0 && nl > 257)){ - if (result == Z_DATA_ERROR){ - z.msg = "oversubscribed distance tree"; - } - else if (result == Z_BUF_ERROR) { - z.msg = "incomplete distance tree"; - result = Z_DATA_ERROR; - } - else if (result != Z_MEM_ERROR){ - z.msg = "empty distance tree with lengths"; - result = Z_DATA_ERROR; - } - return result; - } - - return Z_OK; - } - - static int inflate_trees_fixed(int[] bl, //literal desired/actual bit depth - int[] bd, //distance desired/actual bit depth - int[][] tl,//literal/length tree result - int[][] td,//distance tree result - ZStream z //for memory allocation - ){ - bl[0]=fixed_bl; - bd[0]=fixed_bd; - tl[0]=fixed_tl; - td[0]=fixed_td; - return Z_OK; - } - - private void initWorkArea(int vsize){ - if(hn==null){ - hn=new int[1]; - v=new int[vsize]; - c=new int[BMAX+1]; - r=new int[3]; - u=new int[BMAX]; - x=new int[BMAX+1]; - } - if(v.lengthstate); - return Z_OK; - } - - int inflateInit(ZStream z, int w){ - z.msg = null; - blocks = null; - - // handle undocumented nowrap option (no zlib header or check) - nowrap = 0; - if(w < 0){ - w = - w; - nowrap = 1; - } - - // set window size - if(w<8 ||w>15){ - inflateEnd(z); - return Z_STREAM_ERROR; - } - wbits=w; - - z.istate.blocks=new InfBlocks(z, - z.istate.nowrap!=0 ? null : this, - 1<>4)+8>z.istate.wbits){ - z.istate.mode = BAD; - z.msg="invalid window size"; - z.istate.marker = 5; // can't try inflateSync - break; - } - z.istate.mode=FLAG; - case FLAG: - - if(z.avail_in==0)return r;r=f; - - z.avail_in--; z.total_in++; - b = (z.next_in[z.next_in_index++])&0xff; - - if((((z.istate.method << 8)+b) % 31)!=0){ - z.istate.mode = BAD; - z.msg = "incorrect header check"; - z.istate.marker = 5; // can't try inflateSync - break; - } - - if((b&PRESET_DICT)==0){ - z.istate.mode = BLOCKS; - break; - } - z.istate.mode = DICT4; - case DICT4: - - if(z.avail_in==0)return r;r=f; - - z.avail_in--; z.total_in++; - z.istate.need=((z.next_in[z.next_in_index++]&0xff)<<24)&0xff000000L; - z.istate.mode=DICT3; - case DICT3: - - if(z.avail_in==0)return r;r=f; - - z.avail_in--; z.total_in++; - z.istate.need+=((z.next_in[z.next_in_index++]&0xff)<<16)&0xff0000L; - z.istate.mode=DICT2; - case DICT2: - - if(z.avail_in==0)return r;r=f; - - z.avail_in--; z.total_in++; - z.istate.need+=((z.next_in[z.next_in_index++]&0xff)<<8)&0xff00L; - z.istate.mode=DICT1; - case DICT1: - - if(z.avail_in==0)return r;r=f; - - z.avail_in--; z.total_in++; - z.istate.need += (z.next_in[z.next_in_index++]&0xffL); - z.adler = z.istate.need; - z.istate.mode = DICT0; - return Z_NEED_DICT; - case DICT0: - z.istate.mode = BAD; - z.msg = "need dictionary"; - z.istate.marker = 0; // can try inflateSync - return Z_STREAM_ERROR; - case BLOCKS: - - r = z.istate.blocks.proc(z, r); - if(r == Z_DATA_ERROR){ - z.istate.mode = BAD; - z.istate.marker = 0; // can try inflateSync - break; - } - if(r == Z_OK){ - r = f; - } - if(r != Z_STREAM_END){ - return r; - } - r = f; - z.istate.blocks.reset(z, z.istate.was); - if(z.istate.nowrap!=0){ - z.istate.mode=DONE; - break; - } - z.istate.mode=CHECK4; - case CHECK4: - - if(z.avail_in==0)return r;r=f; - - z.avail_in--; z.total_in++; - z.istate.need=((z.next_in[z.next_in_index++]&0xff)<<24)&0xff000000L; - z.istate.mode=CHECK3; - case CHECK3: - - if(z.avail_in==0)return r;r=f; - - z.avail_in--; z.total_in++; - z.istate.need+=((z.next_in[z.next_in_index++]&0xff)<<16)&0xff0000L; - z.istate.mode = CHECK2; - case CHECK2: - - if(z.avail_in==0)return r;r=f; - - z.avail_in--; z.total_in++; - z.istate.need+=((z.next_in[z.next_in_index++]&0xff)<<8)&0xff00L; - z.istate.mode = CHECK1; - case CHECK1: - - if(z.avail_in==0)return r;r=f; - - z.avail_in--; z.total_in++; - z.istate.need+=(z.next_in[z.next_in_index++]&0xffL); - - if(((int)(z.istate.was[0])) != ((int)(z.istate.need))){ - z.istate.mode = BAD; - z.msg = "incorrect data check"; - z.istate.marker = 5; // can't try inflateSync - break; - } - - z.istate.mode = DONE; - case DONE: - return Z_STREAM_END; - case BAD: - return Z_DATA_ERROR; - default: - return Z_STREAM_ERROR; - } - } - } - - - int inflateSetDictionary(ZStream z, byte[] dictionary, int dictLength){ - int index=0; - int length = dictLength; - if(z==null || z.istate == null|| z.istate.mode != DICT0) - return Z_STREAM_ERROR; - - if(z._adler.adler32(1L, dictionary, 0, dictLength)!=z.adler){ - return Z_DATA_ERROR; - } - - z.adler = z._adler.adler32(0, null, 0, 0); - - if(length >= (1<>>7)]); - } - - short[] dyn_tree; // the dynamic tree - int max_code; // largest code with non zero frequency - StaticTree stat_desc; // the corresponding static tree - - // Compute the optimal bit lengths for a tree and update the total bit length - // for the current block. - // IN assertion: the fields freq and dad are set, heap[heap_max] and - // above are the tree nodes sorted by increasing frequency. - // OUT assertions: the field len is set to the optimal bit length, the - // array bl_count contains the frequencies for each bit length. - // The length opt_len is updated; static_len is also updated if stree is - // not null. - void gen_bitlen(Deflate s){ - short[] tree = dyn_tree; - short[] stree = stat_desc.static_tree; - int[] extra = stat_desc.extra_bits; - int base = stat_desc.extra_base; - int max_length = stat_desc.max_length; - int h; // heap index - int n, m; // iterate over the tree elements - int bits; // bit length - int xbits; // extra bits - short f; // frequency - int overflow = 0; // number of elements with bit length too large - - for (bits = 0; bits <= MAX_BITS; bits++) s.bl_count[bits] = 0; - - // In a first pass, compute the optimal bit lengths (which may - // overflow in the case of the bit length tree). - tree[s.heap[s.heap_max]*2+1] = 0; // root of the heap - - for(h=s.heap_max+1; h max_length){ bits = max_length; overflow++; } - tree[n*2+1] = (short)bits; - // We overwrite tree[n*2+1] which is no longer needed - - if (n > max_code) continue; // not a leaf node - - s.bl_count[bits]++; - xbits = 0; - if (n >= base) xbits = extra[n-base]; - f = tree[n*2]; - s.opt_len += f * (bits + xbits); - if (stree!=null) s.static_len += f * (stree[n*2+1] + xbits); - } - if (overflow == 0) return; - - // This happens for example on obj2 and pic of the Calgary corpus - // Find the first bit length which could increase: - do { - bits = max_length-1; - while(s.bl_count[bits]==0) bits--; - s.bl_count[bits]--; // move one leaf down the tree - s.bl_count[bits+1]+=2; // move one overflow item as its brother - s.bl_count[max_length]--; - // The brother of the overflow item also moves one step up, - // but this does not affect bl_count[max_length] - overflow -= 2; - } - while (overflow > 0); - - for (bits = max_length; bits != 0; bits--) { - n = s.bl_count[bits]; - while (n != 0) { - m = s.heap[--h]; - if (m > max_code) continue; - if (tree[m*2+1] != bits) { - s.opt_len += ((long)bits - (long)tree[m*2+1])*(long)tree[m*2]; - tree[m*2+1] = (short)bits; - } - n--; - } - } - } - - // Construct one Huffman tree and assigns the code bit strings and lengths. - // Update the total bit length for the current block. - // IN assertion: the field freq is set for all tree elements. - // OUT assertions: the fields len and code are set to the optimal bit length - // and corresponding code. The length opt_len is updated; static_len is - // also updated if stree is not null. The field max_code is set. - void build_tree(Deflate s){ - short[] tree=dyn_tree; - short[] stree=stat_desc.static_tree; - int elems=stat_desc.elems; - int n, m; // iterate over heap elements - int max_code=-1; // largest code with non zero frequency - int node; // new node being created - - // Construct the initial heap, with least frequent element in - // heap[1]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. - // heap[0] is not used. - s.heap_len = 0; - s.heap_max = HEAP_SIZE; - - for(n=0; n=1; n--) - s.pqdownheap(tree, n); - - // Construct the Huffman tree by repeatedly combining the least two - // frequent nodes. - - node=elems; // next internal node of the tree - do{ - // n = node of least frequency - n=s.heap[1]; - s.heap[1]=s.heap[s.heap_len--]; - s.pqdownheap(tree, 1); - m=s.heap[1]; // m = node of next least frequency - - s.heap[--s.heap_max] = n; // keep the nodes sorted by frequency - s.heap[--s.heap_max] = m; - - // Create a new node father of n and m - tree[node*2] = (short)(tree[n*2] + tree[m*2]); - s.depth[node] = (byte)(Math.max(s.depth[n],s.depth[m])+1); - tree[n*2+1] = tree[m*2+1] = (short)node; - - // and insert the new node in the heap - s.heap[1] = node++; - s.pqdownheap(tree, 1); - } - while(s.heap_len>=2); - - s.heap[--s.heap_max] = s.heap[1]; - - // At this point, the fields freq and dad are set. We can now - // generate the bit lengths. - - gen_bitlen(s); - - // The field len is now set, we can generate the bit codes - gen_codes(tree, max_code, s.bl_count); - } - - // Generate the codes for a given tree and bit counts (which need not be - // optimal). - // IN assertion: the array bl_count contains the bit length statistics for - // the given tree and the field len is set for all tree elements. - // OUT assertion: the field code is set for all tree elements of non - // zero code length. - static void gen_codes(short[] tree, // the tree to decorate - int max_code, // largest code with non zero frequency - short[] bl_count // number of codes at each bit length - ){ - short[] next_code=new short[MAX_BITS+1]; // next code value for each bit length - short code = 0; // running code value - int bits; // bit index - int n; // code index - - // The distribution counts are first used to generate the code values - // without bit reversal. - for (bits = 1; bits <= MAX_BITS; bits++) { - next_code[bits] = code = (short)((code + bl_count[bits-1]) << 1); - } - - // Check that the bit counts in bl_count are consistent. The last code - // must be all ones. - //Assert (code + bl_count[MAX_BITS]-1 == (1<>>=1; - res<<=1; - } - while(--len>0); - return res>>>1; - } -} - diff --git a/app/src/main/java/com/jcraft/jzlib/ZInputStream.java b/app/src/main/java/com/jcraft/jzlib/ZInputStream.java deleted file mode 100644 index 3f97c4483b..0000000000 --- a/app/src/main/java/com/jcraft/jzlib/ZInputStream.java +++ /dev/null @@ -1,149 +0,0 @@ -/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ -/* -Copyright (c) 2001 Lapo Luchini. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - - 3. The names of the authors may not be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS -OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, -OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* - * This program is based on zlib-1.1.3, so all credit should go authors - * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) - * and contributors of zlib. - */ - -package com.jcraft.jzlib; -import java.io.*; - -public class ZInputStream extends FilterInputStream { - - protected ZStream z=new ZStream(); - protected int bufsize=512; - protected int flush=JZlib.Z_NO_FLUSH; - protected byte[] buf=new byte[bufsize], - buf1=new byte[1]; - protected boolean compress; - - protected InputStream in=null; - - public ZInputStream(InputStream in) { - this(in, false); - } - public ZInputStream(InputStream in, boolean nowrap) { - super(in); - this.in=in; - z.inflateInit(nowrap); - compress=false; - z.next_in=buf; - z.next_in_index=0; - z.avail_in=0; - } - - public ZInputStream(InputStream in, int level) { - super(in); - this.in=in; - z.deflateInit(level); - compress=true; - z.next_in=buf; - z.next_in_index=0; - z.avail_in=0; - } - - /*public int available() throws IOException { - return inf.finished() ? 0 : 1; - }*/ - - public int read() throws IOException { - if(read(buf1, 0, 1)==-1) - return(-1); - return(buf1[0]&0xFF); - } - - private boolean nomoreinput=false; - - public int read(byte[] b, int off, int len) throws IOException { - if(len==0) - return(0); - int err; - z.next_out=b; - z.next_out_index=off; - z.avail_out=len; - do { - if((z.avail_in==0)&&(!nomoreinput)) { // if buffer is empty and more input is avaiable, refill it - z.next_in_index=0; - z.avail_in=in.read(buf, 0, bufsize);//(bufsize0 || z.avail_out==0); - } - - public int getFlushMode() { - return(flush); - } - - public void setFlushMode(int flush) { - this.flush=flush; - } - - public void finish() throws IOException { - int err; - do{ - z.next_out=buf; - z.next_out_index=0; - z.avail_out=bufsize; - if(compress){ err=z.deflate(JZlib.Z_FINISH); } - else{ err=z.inflate(JZlib.Z_FINISH); } - if(err!=JZlib.Z_STREAM_END && err != JZlib.Z_OK) - throw new ZStreamException((compress?"de":"in")+"flating: "+z.msg); - if(bufsize-z.avail_out>0){ - out.write(buf, 0, bufsize-z.avail_out); - } - } - while(z.avail_in>0 || z.avail_out==0); - flush(); - } - public void end() { - if(z==null) - return; - if(compress){ z.deflateEnd(); } - else{ z.inflateEnd(); } - z.free(); - z=null; - } - public void close() throws IOException { - try{ - try{finish();} - catch (IOException ignored) {} - } - finally{ - end(); - out.close(); - out=null; - } - } - - /** - * Returns the total number of bytes input so far. - */ - public long getTotalIn() { - return z.total_in; - } - - /** - * Returns the total number of bytes output so far. - */ - public long getTotalOut() { - return z.total_out; - } - - public void flush() throws IOException { - out.flush(); - } - -} diff --git a/app/src/main/java/com/jcraft/jzlib/ZStream.java b/app/src/main/java/com/jcraft/jzlib/ZStream.java deleted file mode 100644 index 334475e987..0000000000 --- a/app/src/main/java/com/jcraft/jzlib/ZStream.java +++ /dev/null @@ -1,211 +0,0 @@ -/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ -/* -Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - - 3. The names of the authors may not be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, -INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, -OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* - * This program is based on zlib-1.1.3, so all credit should go authors - * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) - * and contributors of zlib. - */ - -package com.jcraft.jzlib; - -final public class ZStream{ - - static final private int MAX_WBITS=15; // 32K LZ77 window - static final private int DEF_WBITS=MAX_WBITS; - - static final private int Z_NO_FLUSH=0; - static final private int Z_PARTIAL_FLUSH=1; - static final private int Z_SYNC_FLUSH=2; - static final private int Z_FULL_FLUSH=3; - static final private int Z_FINISH=4; - - static final private int MAX_MEM_LEVEL=9; - - static final private int Z_OK=0; - static final private int Z_STREAM_END=1; - static final private int Z_NEED_DICT=2; - static final private int Z_ERRNO=-1; - static final private int Z_STREAM_ERROR=-2; - static final private int Z_DATA_ERROR=-3; - static final private int Z_MEM_ERROR=-4; - static final private int Z_BUF_ERROR=-5; - static final private int Z_VERSION_ERROR=-6; - - public byte[] next_in; // next input byte - public int next_in_index; - public int avail_in; // number of bytes available at next_in - public long total_in; // total nb of input bytes read so far - - public byte[] next_out; // next output byte should be put there - public int next_out_index; - public int avail_out; // remaining free space at next_out - public long total_out; // total nb of bytes output so far - - public String msg; - - Deflate dstate; - Inflate istate; - - int data_type; // best guess about the data type: ascii or binary - - public long adler; - Adler32 _adler=new Adler32(); - - public int inflateInit(){ - return inflateInit(DEF_WBITS); - } - public int inflateInit(boolean nowrap){ - return inflateInit(DEF_WBITS, nowrap); - } - public int inflateInit(int w){ - return inflateInit(w, false); - } - - public int inflateInit(int w, boolean nowrap){ - istate=new Inflate(); - return istate.inflateInit(this, nowrap?-w:w); - } - - public int inflate(int f){ - if(istate==null) return Z_STREAM_ERROR; - return istate.inflate(this, f); - } - public int inflateEnd(){ - if(istate==null) return Z_STREAM_ERROR; - int ret=istate.inflateEnd(this); - istate = null; - return ret; - } - public int inflateSync(){ - if(istate == null) - return Z_STREAM_ERROR; - return istate.inflateSync(this); - } - public int inflateSetDictionary(byte[] dictionary, int dictLength){ - if(istate == null) - return Z_STREAM_ERROR; - return istate.inflateSetDictionary(this, dictionary, dictLength); - } - - public int deflateInit(int level){ - return deflateInit(level, MAX_WBITS); - } - public int deflateInit(int level, boolean nowrap){ - return deflateInit(level, MAX_WBITS, nowrap); - } - public int deflateInit(int level, int bits){ - return deflateInit(level, bits, false); - } - public int deflateInit(int level, int bits, boolean nowrap){ - dstate=new Deflate(); - return dstate.deflateInit(this, level, nowrap?-bits:bits); - } - public int deflate(int flush){ - if(dstate==null){ - return Z_STREAM_ERROR; - } - return dstate.deflate(this, flush); - } - public int deflateEnd(){ - if(dstate==null) return Z_STREAM_ERROR; - int ret=dstate.deflateEnd(); - dstate=null; - return ret; - } - public int deflateParams(int level, int strategy){ - if(dstate==null) return Z_STREAM_ERROR; - return dstate.deflateParams(this, level, strategy); - } - public int deflateSetDictionary (byte[] dictionary, int dictLength){ - if(dstate == null) - return Z_STREAM_ERROR; - return dstate.deflateSetDictionary(this, dictionary, dictLength); - } - - // Flush as much pending output as possible. All deflate() output goes - // through this function so some applications may wish to modify it - // to avoid allocating a large strm->next_out buffer and copying into it. - // (See also read_buf()). - void flush_pending(){ - int len=dstate.pending; - - if(len>avail_out) len=avail_out; - if(len==0) return; - - if(dstate.pending_buf.length<=dstate.pending_out || - next_out.length<=next_out_index || - dstate.pending_buf.length<(dstate.pending_out+len) || - next_out.length<(next_out_index+len)){ - System.out.println(dstate.pending_buf.length+", "+dstate.pending_out+ - ", "+next_out.length+", "+next_out_index+", "+len); - System.out.println("avail_out="+avail_out); - } - - System.arraycopy(dstate.pending_buf, dstate.pending_out, - next_out, next_out_index, len); - - next_out_index+=len; - dstate.pending_out+=len; - total_out+=len; - avail_out-=len; - dstate.pending-=len; - if(dstate.pending==0){ - dstate.pending_out=0; - } - } - - // Read a new buffer from the current input stream, update the adler32 - // and total number of bytes read. All deflate() input goes through - // this function so some applications may wish to modify it to avoid - // allocating a large strm->next_in buffer and copying from it. - // (See also flush_pending()). - int read_buf(byte[] buf, int start, int size) { - int len=avail_in; - - if(len>size) len=size; - if(len==0) return 0; - - avail_in-=len; - - if(dstate.noheader==0) { - adler=_adler.adler32(adler, next_in, next_in_index, len); - } - System.arraycopy(next_in, next_in_index, buf, start, len); - next_in_index += len; - total_in += len; - return len; - } - - public void free(){ - next_in=null; - next_out=null; - msg=null; - _adler=null; - } -} diff --git a/app/src/main/java/com/jcraft/jzlib/ZStreamException.java b/app/src/main/java/com/jcraft/jzlib/ZStreamException.java deleted file mode 100644 index 308bb8a1e5..0000000000 --- a/app/src/main/java/com/jcraft/jzlib/ZStreamException.java +++ /dev/null @@ -1,44 +0,0 @@ -/* -*-mode:java; c-basic-offset:2; -*- */ -/* -Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - - 3. The names of the authors may not be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, -INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, -OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* - * This program is based on zlib-1.1.3, so all credit should go authors - * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) - * and contributors of zlib. - */ - -package com.jcraft.jzlib; - -public class ZStreamException extends java.io.IOException { - public ZStreamException() { - super(); - } - public ZStreamException(String s) { - super(s); - } -} diff --git a/app/src/main/java/com/novell/sasl/client/DigestChallenge.java b/app/src/main/java/com/novell/sasl/client/DigestChallenge.java index 90e6247ea9..6c2a5da774 100644 --- a/app/src/main/java/com/novell/sasl/client/DigestChallenge.java +++ b/app/src/main/java/com/novell/sasl/client/DigestChallenge.java @@ -15,41 +15,40 @@ package com.novell.sasl.client; import java.util.*; + import org.apache.harmony.javax.security.sasl.*; /** * Implements the DigestChallenge class which will be used by the * DigestMD5SaslClient class */ -class DigestChallenge extends Object -{ - public static final int QOP_AUTH = 0x01; - public static final int QOP_AUTH_INT = 0x02; - public static final int QOP_AUTH_CONF = 0x04; - public static final int QOP_UNRECOGNIZED = 0x08; - - private static final int CIPHER_3DES = 0x01; - private static final int CIPHER_DES = 0x02; - private static final int CIPHER_RC4_40 = 0x04; - private static final int CIPHER_RC4 = 0x08; - private static final int CIPHER_RC4_56 = 0x10; - private static final int CIPHER_UNRECOGNIZED = 0x20; +class DigestChallenge extends Object { + public static final int QOP_AUTH = 0x01; + public static final int QOP_AUTH_INT = 0x02; + public static final int QOP_AUTH_CONF = 0x04; + public static final int QOP_UNRECOGNIZED = 0x08; + + private static final int CIPHER_3DES = 0x01; + private static final int CIPHER_DES = 0x02; + private static final int CIPHER_RC4_40 = 0x04; + private static final int CIPHER_RC4 = 0x08; + private static final int CIPHER_RC4_56 = 0x10; + private static final int CIPHER_UNRECOGNIZED = 0x20; private static final int CIPHER_RECOGNIZED_MASK = - CIPHER_3DES | CIPHER_DES | CIPHER_RC4_40 | CIPHER_RC4 | CIPHER_RC4_56; + CIPHER_3DES | CIPHER_DES | CIPHER_RC4_40 | CIPHER_RC4 | CIPHER_RC4_56; private ArrayList m_realms; - private String m_nonce; - private int m_qop; - private boolean m_staleFlag; - private int m_maxBuf; - private String m_characterSet; - private String m_algorithm; - private int m_cipherOptions; + private String m_nonce; + private int m_qop; + private boolean m_staleFlag; + private int m_maxBuf; + private String m_characterSet; + private String m_algorithm; + private int m_cipherOptions; DigestChallenge( - byte[] challenge) - throws SaslException - { + byte[] challenge) + throws SaslException { m_realms = new ArrayList(5); m_nonce = null; m_qop = 0; @@ -60,13 +59,10 @@ class DigestChallenge extends Object m_cipherOptions = 0; DirectiveList dirList = new DirectiveList(challenge); - try - { + try { dirList.parseDirectives(); checkSemantics(dirList); - } - catch (SaslException e) - { + } catch (SaslException e) { } } @@ -74,69 +70,64 @@ class DigestChallenge extends Object * Checks the semantics of the directives in the directive list as parsed * from the digest challenge byte array. * - * @param dirList the list of directives parsed from the digest challenge - * - * @exception SaslException If a semantic error occurs + * @param dirList the list of directives parsed from the digest challenge + * @throws SaslException If a semantic error occurs */ void checkSemantics( - DirectiveList dirList) throws SaslException - { - Iterator directives = dirList.getIterator(); - ParsedDirective directive; - String name; - - while (directives.hasNext()) - { - directive = (ParsedDirective)directives.next(); - name = directive.getName(); - if (name.equals("realm")) - handleRealm(directive); - else if (name.equals("nonce")) - handleNonce(directive); - else if (name.equals("qop")) - handleQop(directive); - else if (name.equals("maxbuf")) - handleMaxbuf(directive); - else if (name.equals("charset")) - handleCharset(directive); - else if (name.equals("algorithm")) - handleAlgorithm(directive); - else if (name.equals("cipher")) - handleCipher(directive); - else if (name.equals("stale")) - handleStale(directive); - } + DirectiveList dirList) throws SaslException { + Iterator directives = dirList.getIterator(); + ParsedDirective directive; + String name; + + while (directives.hasNext()) { + directive = (ParsedDirective) directives.next(); + name = directive.getName(); + if (name.equals("realm")) + handleRealm(directive); + else if (name.equals("nonce")) + handleNonce(directive); + else if (name.equals("qop")) + handleQop(directive); + else if (name.equals("maxbuf")) + handleMaxbuf(directive); + else if (name.equals("charset")) + handleCharset(directive); + else if (name.equals("algorithm")) + handleAlgorithm(directive); + else if (name.equals("cipher")) + handleCipher(directive); + else if (name.equals("stale")) + handleStale(directive); + } /* post semantic check */ - if (-1 == m_maxBuf) - m_maxBuf = 65536; - - if (m_qop == 0) - m_qop = QOP_AUTH; - else if ( (m_qop & QOP_AUTH) != QOP_AUTH ) - throw new SaslException("Only qop-auth is supported by client"); - else if ( ((m_qop & QOP_AUTH_CONF) == QOP_AUTH_CONF) && - (0 == (m_cipherOptions & CIPHER_RECOGNIZED_MASK)) ) - throw new SaslException("Invalid cipher options"); - else if (null == m_nonce) - throw new SaslException("Missing nonce directive"); - else if (m_staleFlag) - throw new SaslException("Unexpected stale flag"); - else if ( null == m_algorithm ) - throw new SaslException("Missing algorithm directive"); + if (-1 == m_maxBuf) + m_maxBuf = 65536; + + if (m_qop == 0) + m_qop = QOP_AUTH; + else if ((m_qop & QOP_AUTH) != QOP_AUTH) + throw new SaslException("Only qop-auth is supported by client"); + else if (((m_qop & QOP_AUTH_CONF) == QOP_AUTH_CONF) && + (0 == (m_cipherOptions & CIPHER_RECOGNIZED_MASK))) + throw new SaslException("Invalid cipher options"); + else if (null == m_nonce) + throw new SaslException("Missing nonce directive"); + else if (m_staleFlag) + throw new SaslException("Unexpected stale flag"); + else if (null == m_algorithm) + throw new SaslException("Missing algorithm directive"); } /** * This function implements the semenatics of the nonce directive. * - * @param pd ParsedDirective - * - * @exception SaslException If an error occurs due to too many nonce - * values + * @param pd ParsedDirective + * @throws SaslException If an error occurs due to too many nonce + * values */ void handleNonce( - ParsedDirective pd) throws SaslException - { + ParsedDirective pd) throws SaslException { if (null != m_nonce) throw new SaslException("Too many nonce values."); @@ -146,31 +137,28 @@ void handleNonce( /** * This function implements the semenatics of the realm directive. * - * @param pd ParsedDirective + * @param pd ParsedDirective */ void handleRealm( - ParsedDirective pd) - { + ParsedDirective pd) { m_realms.add(pd.getValue()); } /** * This function implements the semenatics of the qop (quality of protection) * directive. The value of the qop directive is as defined below: - * qop-options = "qop" "=" <"> qop-list <"> - * qop-list = 1#qop-value - * qop-value = "auth" | "auth-int" | "auth-conf" | token - * - * @param pd ParsedDirective + * qop-options = "qop" "=" <"> qop-list <"> + * qop-list = 1#qop-value + * qop-value = "auth" | "auth-int" | "auth-conf" | token * - * @exception SaslException If an error occurs due to too many qop - * directives + * @param pd ParsedDirective + * @throws SaslException If an error occurs due to too many qop + * directives */ void handleQop( - ParsedDirective pd) throws SaslException - { - String token; - TokenParser parser; + ParsedDirective pd) throws SaslException { + String token; + TokenParser parser; if (m_qop != 0) throw new SaslException("Too many qop directives."); @@ -178,12 +166,11 @@ void handleQop( parser = new TokenParser(pd.getValue()); for (token = parser.parseToken(); token != null; - token = parser.parseToken()) - { + token = parser.parseToken()) { if (token.equals("auth")) - m_qop |= QOP_AUTH; - else if (token.equals("auth-int")) - m_qop |= QOP_AUTH_INT; + m_qop |= QOP_AUTH; + else if (token.equals("auth-int")) + m_qop |= QOP_AUTH_INT; else if (token.equals("auth-conf")) m_qop |= QOP_AUTH_CONF; else @@ -195,13 +182,11 @@ else if (token.equals("auth-conf")) * This function implements the semenatics of the Maxbuf directive. * the value is defined as: 1*DIGIT * - * @param pd ParsedDirective - * - * @exception SaslException If an error occur + * @param pd ParsedDirective + * @throws SaslException If an error occur */ void handleMaxbuf( - ParsedDirective pd) throws SaslException - { + ParsedDirective pd) throws SaslException { if (-1 != m_maxBuf) /*it's initialized to -1 */ throw new SaslException("Too many maxBuf directives."); @@ -215,15 +200,13 @@ void handleMaxbuf( * This function implements the semenatics of the charset directive. * the value is defined as: 1*DIGIT * - * @param pd ParsedDirective - * - * @exception SaslException If an error occurs dur to too many charset - * directives or Invalid character encoding - * directive + * @param pd ParsedDirective + * @throws SaslException If an error occurs dur to too many charset + * directives or Invalid character encoding + * directive */ void handleCharset( - ParsedDirective pd) throws SaslException - { + ParsedDirective pd) throws SaslException { if (null != m_characterSet) throw new SaslException("Too many charset directives."); @@ -237,41 +220,37 @@ void handleCharset( * This function implements the semenatics of the charset directive. * the value is defined as: 1*DIGIT * - * @param pd ParsedDirective - * - * @exception SaslException If an error occurs due to too many algorith - * directive or Invalid algorithm directive - * value + * @param pd ParsedDirective + * @throws SaslException If an error occurs due to too many algorith + * directive or Invalid algorithm directive + * value */ void handleAlgorithm( - ParsedDirective pd) throws SaslException - { + ParsedDirective pd) throws SaslException { if (null != m_algorithm) throw new SaslException("Too many algorithm directives."); - m_algorithm = pd.getValue(); + m_algorithm = pd.getValue(); if (!"md5-sess".equals(m_algorithm)) throw new SaslException("Invalid algorithm directive value: " + - m_algorithm); + m_algorithm); } /** * This function implements the semenatics of the cipher-opts directive * directive. The value of the qop directive is as defined below: - * qop-options = "qop" "=" <"> qop-list <"> - * qop-list = 1#qop-value - * qop-value = "auth" | "auth-int" | "auth-conf" | token + * qop-options = "qop" "=" <"> qop-list <"> + * qop-list = 1#qop-value + * qop-value = "auth" | "auth-int" | "auth-conf" | token * - * @param pd ParsedDirective - * - * @exception SaslException If an error occurs due to Too many cipher - * directives + * @param pd ParsedDirective + * @throws SaslException If an error occurs due to Too many cipher + * directives */ void handleCipher( - ParsedDirective pd) throws SaslException - { - String token; + ParsedDirective pd) throws SaslException { + String token; TokenParser parser; if (0 != m_cipherOptions) @@ -281,12 +260,11 @@ void handleCipher( token = parser.parseToken(); for (token = parser.parseToken(); token != null; - token = parser.parseToken()) - { - if ("3des".equals(token)) - m_cipherOptions |= CIPHER_3DES; - else if ("des".equals(token)) - m_cipherOptions |= CIPHER_DES; + token = parser.parseToken()) { + if ("3des".equals(token)) + m_cipherOptions |= CIPHER_3DES; + else if ("des".equals(token)) + m_cipherOptions |= CIPHER_DES; else if ("rc4-40".equals(token)) m_cipherOptions |= CIPHER_RC4_40; else if ("rc4".equals(token)) @@ -304,14 +282,12 @@ else if ("rc4-56".equals(token)) /** * This function implements the semenatics of the stale directive. * - * @param pd ParsedDirective - * - * @exception SaslException If an error occurs due to Too many stale - * directives or Invalid stale directive value + * @param pd ParsedDirective + * @throws SaslException If an error occurs due to Too many stale + * directives or Invalid stale directive value */ void handleStale( - ParsedDirective pd) throws SaslException - { + ParsedDirective pd) throws SaslException { if (false != m_staleFlag) throw new SaslException("Too many stale directives."); @@ -319,74 +295,66 @@ void handleStale( m_staleFlag = true; else throw new SaslException("Invalid stale directive value: " + - pd.getValue()); + pd.getValue()); } /** * Return the list of the All the Realms * - * @return List of all the realms + * @return List of all the realms */ - public ArrayList getRealms() - { + public ArrayList getRealms() { return m_realms; } /** * @return Returns the Nonce */ - public String getNonce() - { + public String getNonce() { return m_nonce; } /** * Return the quality-of-protection - * + * * @return The quality-of-protection */ - public int getQop() - { + public int getQop() { return m_qop; } /** * @return The state of the Staleflag */ - public boolean getStaleFlag() - { + public boolean getStaleFlag() { return m_staleFlag; } /** * @return The Maximum Buffer value */ - public int getMaxBuf() - { + public int getMaxBuf() { return m_maxBuf; } /** * @return character set values as string */ - public String getCharacterSet() - { + public String getCharacterSet() { return m_characterSet; } /** * @return The String value of the algorithm */ - public String getAlgorithm() - { + public String getAlgorithm() { return m_algorithm; } /** * @return The cipher options */ - public int getCipherOptions() - { + public int getCipherOptions() { return m_cipherOptions; } } diff --git a/app/src/main/java/com/novell/sasl/client/DigestMD5SaslClient.java b/app/src/main/java/com/novell/sasl/client/DigestMD5SaslClient.java index 141c96b3df..d86879649f 100644 --- a/app/src/main/java/com/novell/sasl/client/DigestMD5SaslClient.java +++ b/app/src/main/java/com/novell/sasl/client/DigestMD5SaslClient.java @@ -16,6 +16,7 @@ import org.apache.harmony.javax.security.sasl.*; import org.apache.harmony.javax.security.auth.callback.*; + import java.security.SecureRandom; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -26,30 +27,29 @@ /** * Implements the Client portion of DigestMD5 Sasl mechanism. */ -public class DigestMD5SaslClient implements SaslClient -{ - private String m_authorizationId = ""; - private String m_protocol = ""; - private String m_serverName = ""; - private Map m_props; - private CallbackHandler m_cbh; - private int m_state; - private String m_qopValue = ""; - private char[] m_HA1 = null; - private String m_digestURI; - private DigestChallenge m_dc; - private String m_clientNonce = ""; - private String m_realm = ""; - private String m_name = ""; - - private static final int STATE_INITIAL = 0; - private static final int STATE_DIGEST_RESPONSE_SENT = 1; - private static final int STATE_VALID_SERVER_RESPONSE = 2; - private static final int STATE_INVALID_SERVER_RESPONSE = 3; - private static final int STATE_DISPOSED = 4; - - private static final int NONCE_BYTE_COUNT = 32; - private static final int NONCE_HEX_COUNT = 2*NONCE_BYTE_COUNT; +public class DigestMD5SaslClient implements SaslClient { + private String m_authorizationId = ""; + private String m_protocol = ""; + private String m_serverName = ""; + private Map m_props; + private CallbackHandler m_cbh; + private int m_state; + private String m_qopValue = ""; + private char[] m_HA1 = null; + private String m_digestURI; + private DigestChallenge m_dc; + private String m_clientNonce = ""; + private String m_realm = ""; + private String m_name = ""; + + private static final int STATE_INITIAL = 0; + private static final int STATE_DIGEST_RESPONSE_SENT = 1; + private static final int STATE_VALID_SERVER_RESPONSE = 2; + private static final int STATE_INVALID_SERVER_RESPONSE = 3; + private static final int STATE_DISPOSED = 4; + + private static final int NONCE_BYTE_COUNT = 32; + private static final int NONCE_HEX_COUNT = 2 * NONCE_BYTE_COUNT; private static final String DIGEST_METHOD = "AUTHENTICATE"; @@ -58,57 +58,50 @@ public class DigestMD5SaslClient implements SaslClient * Assumes that the QOP, STRENGTH, and SERVER_AUTH properties are * contained in props * - * @param authorizationId The possibly null protocol-dependent - * identification to be used for authorization. If - * null or empty, the server derives an authorization - * ID from the client's authentication credentials. - * When the SASL authentication completes - * successfully, the specified entity is granted - * access. - * - * @param protocol The non-null string name of the protocol for which - * the authentication is being performed (e.g. "ldap") - * - * @param serverName The non-null fully qualified host name of the server - * to authenticate to - * - * @param props The possibly null set of properties used to select - * the SASL mechanism and to configure the - * authentication exchange of the selected mechanism. - * See the Sasl class for a list of standard properties. - * Other, possibly mechanism-specific, properties can - * be included. Properties not relevant to the selected - * mechanism are ignored. - * - * @param cbh The possibly null callback handler to used by the - * SASL mechanisms to get further information from the - * application/library to complete the authentication. - * For example, a SASL mechanism might require the - * authentication ID, password and realm from the - * caller. The authentication ID is requested by using - * a NameCallback. The password is requested by using - * a PasswordCallback. The realm is requested by using - * a RealmChoiceCallback if there is a list of realms - * to choose from, and by using a RealmCallback if the - * realm must be entered. - * - * @return A possibly null SaslClient created using the - * parameters supplied. If null, this factory cannot - * produce a SaslClient using the parameters supplied. - * - * @exception SaslException If a SaslClient instance cannot be created - * because of an error + * @param authorizationId The possibly null protocol-dependent + * identification to be used for authorization. If + * null or empty, the server derives an authorization + * ID from the client's authentication credentials. + * When the SASL authentication completes + * successfully, the specified entity is granted + * access. + * @param protocol The non-null string name of the protocol for which + * the authentication is being performed (e.g. "ldap") + * @param serverName The non-null fully qualified host name of the server + * to authenticate to + * @param props The possibly null set of properties used to select + * the SASL mechanism and to configure the + * authentication exchange of the selected mechanism. + * See the Sasl class for a list of standard properties. + * Other, possibly mechanism-specific, properties can + * be included. Properties not relevant to the selected + * mechanism are ignored. + * @param cbh The possibly null callback handler to used by the + * SASL mechanisms to get further information from the + * application/library to complete the authentication. + * For example, a SASL mechanism might require the + * authentication ID, password and realm from the + * caller. The authentication ID is requested by using + * a NameCallback. The password is requested by using + * a PasswordCallback. The realm is requested by using + * a RealmChoiceCallback if there is a list of realms + * to choose from, and by using a RealmCallback if the + * realm must be entered. + * @return A possibly null SaslClient created using the + * parameters supplied. If null, this factory cannot + * produce a SaslClient using the parameters supplied. + * @throws SaslException If a SaslClient instance cannot be created + * because of an error */ public static SaslClient getClient( - String authorizationId, - String protocol, - String serverName, - Map props, - CallbackHandler cbh) - { - String desiredQOP = (String)props.get(Sasl.QOP); - String desiredStrength = (String)props.get(Sasl.STRENGTH); - String serverAuth = (String)props.get(Sasl.SERVER_AUTH); + String authorizationId, + String protocol, + String serverName, + Map props, + CallbackHandler cbh) { + String desiredQOP = (String) props.get(Sasl.QOP); + String desiredStrength = (String) props.get(Sasl.STRENGTH); + String serverAuth = (String) props.get(Sasl.SERVER_AUTH); //only support qop equal to auth if ((desiredQOP != null) && !"auth".equals(desiredQOP)) @@ -123,7 +116,7 @@ public static SaslClient getClient( return null; return new DigestMD5SaslClient(authorizationId, protocol, - serverName, props, cbh); + serverName, props, cbh); } /** @@ -131,48 +124,42 @@ public static SaslClient getClient( * Assumes that the QOP, STRENGTH, and SERVER_AUTH properties are * contained in props * - * @param authorizationId The possibly null protocol-dependent - * identification to be used for authorization. If - * null or empty, the server derives an authorization - * ID from the client's authentication credentials. - * When the SASL authentication completes - * successfully, the specified entity is granted - * access. - * - * @param protocol The non-null string name of the protocol for which - * the authentication is being performed (e.g. "ldap") - * - * @param serverName The non-null fully qualified host name of the server - * to authenticate to - * - * @param props The possibly null set of properties used to select - * the SASL mechanism and to configure the - * authentication exchange of the selected mechanism. - * See the Sasl class for a list of standard properties. - * Other, possibly mechanism-specific, properties can - * be included. Properties not relevant to the selected - * mechanism are ignored. - * - * @param cbh The possibly null callback handler to used by the - * SASL mechanisms to get further information from the - * application/library to complete the authentication. - * For example, a SASL mechanism might require the - * authentication ID, password and realm from the - * caller. The authentication ID is requested by using - * a NameCallback. The password is requested by using - * a PasswordCallback. The realm is requested by using - * a RealmChoiceCallback if there is a list of realms - * to choose from, and by using a RealmCallback if the - * realm must be entered. - * + * @param authorizationId The possibly null protocol-dependent + * identification to be used for authorization. If + * null or empty, the server derives an authorization + * ID from the client's authentication credentials. + * When the SASL authentication completes + * successfully, the specified entity is granted + * access. + * @param protocol The non-null string name of the protocol for which + * the authentication is being performed (e.g. "ldap") + * @param serverName The non-null fully qualified host name of the server + * to authenticate to + * @param props The possibly null set of properties used to select + * the SASL mechanism and to configure the + * authentication exchange of the selected mechanism. + * See the Sasl class for a list of standard properties. + * Other, possibly mechanism-specific, properties can + * be included. Properties not relevant to the selected + * mechanism are ignored. + * @param cbh The possibly null callback handler to used by the + * SASL mechanisms to get further information from the + * application/library to complete the authentication. + * For example, a SASL mechanism might require the + * authentication ID, password and realm from the + * caller. The authentication ID is requested by using + * a NameCallback. The password is requested by using + * a PasswordCallback. The realm is requested by using + * a RealmChoiceCallback if there is a list of realms + * to choose from, and by using a RealmCallback if the + * realm must be entered. */ - private DigestMD5SaslClient( - String authorizationId, - String protocol, - String serverName, - Map props, - CallbackHandler cbh) - { + private DigestMD5SaslClient( + String authorizationId, + String protocol, + String serverName, + Map props, + CallbackHandler cbh) { m_authorizationId = authorizationId; m_protocol = protocol; m_serverName = serverName; @@ -187,10 +174,9 @@ private DigestMD5SaslClient( * caller should call evaluateChallenge() with an empty array to get the * initial response. * - * @return true if this mechanism has an initial response + * @return true if this mechanism has an initial response */ - public boolean hasInitialResponse() - { + public boolean hasInitialResponse() { return false; } @@ -200,14 +186,13 @@ public boolean hasInitialResponse() * the caller has received indication from the server (in a protocol- * specific manner) that the exchange has completed. * - * @return true if the authentication exchange has completed; - * false otherwise. + * @return true if the authentication exchange has completed; + * false otherwise. */ - public boolean isComplete() - { + public boolean isComplete() { if ((m_state == STATE_VALID_SERVER_RESPONSE) || - (m_state == STATE_INVALID_SERVER_RESPONSE) || - (m_state == STATE_DISPOSED)) + (m_state == STATE_INVALID_SERVER_RESPONSE) || + (m_state == STATE_DISPOSED)) return true; else return false; @@ -219,28 +204,24 @@ public boolean isComplete() * isComplete() returns true) and only if the authentication exchange has * negotiated integrity and/or privacy as the quality of protection; * otherwise, an IllegalStateException is thrown. - * + *

* incoming is the contents of the SASL buffer as defined in RFC 2222 * without the leading four octet field that represents the length. * offset and len specify the portion of incoming to use. * - * @param incoming A non-null byte array containing the encoded bytes - * from the server - * @param offset The starting position at incoming of the bytes to use - * - * @param len The number of bytes from incoming to use - * - * @return A non-null byte array containing the decoded bytes - * + * @param incoming A non-null byte array containing the encoded bytes + * from the server + * @param offset The starting position at incoming of the bytes to use + * @param len The number of bytes from incoming to use + * @return A non-null byte array containing the decoded bytes */ public byte[] unwrap( - byte[] incoming, - int offset, - int len) - throws SaslException - { + byte[] incoming, + int offset, + int len) + throws SaslException { throw new IllegalStateException( - "unwrap: QOP has neither integrity nor privacy>"); + "unwrap: QOP has neither integrity nor privacy>"); } /** @@ -249,31 +230,27 @@ public byte[] unwrap( * isComplete() returns true) and only if the authentication exchange has * negotiated integrity and/or privacy as the quality of protection; * otherwise, an IllegalStateException is thrown. - * + *

* The result of this method will make up the contents of the SASL buffer as * defined in RFC 2222 without the leading four octet field that represents * the length. offset and len specify the portion of outgoing to use. * - * @param outgoing A non-null byte array containing the bytes to encode - * @param offset The starting position at outgoing of the bytes to use - * @param len The number of bytes from outgoing to use - * + * @param outgoing A non-null byte array containing the bytes to encode + * @param offset The starting position at outgoing of the bytes to use + * @param len The number of bytes from outgoing to use * @return A non-null byte array containing the encoded bytes - * - * @exception SaslException if incoming cannot be successfully unwrapped. - * - * @exception IllegalStateException if the authentication exchange has - * not completed, or if the negotiated quality of - * protection has neither integrity nor privacy. + * @throws SaslException if incoming cannot be successfully unwrapped. + * @throws IllegalStateException if the authentication exchange has + * not completed, or if the negotiated quality of + * protection has neither integrity nor privacy. */ public byte[] wrap( - byte[] outgoing, - int offset, - int len) - throws SaslException - { + byte[] outgoing, + int offset, + int len) + throws SaslException { throw new IllegalStateException( - "wrap: QOP has neither integrity nor privacy>"); + "wrap: QOP has neither integrity nor privacy>"); } /** @@ -281,20 +258,17 @@ public byte[] wrap( * the authentication exchange has completed (i.e., when isComplete() * returns true); otherwise, an IllegalStateException is thrown. * - * @param propName The non-null property name - * - * @return The value of the negotiated property. If null, the property was - * not negotiated or is not applicable to this mechanism. - * - * @exception IllegalStateException if this authentication exchange has - * not completed + * @param propName The non-null property name + * @return The value of the negotiated property. If null, the property was + * not negotiated or is not applicable to this mechanism. + * @throws IllegalStateException if this authentication exchange has + * not completed */ public Object getNegotiatedProperty( - String propName) - { + String propName) { if (m_state != STATE_VALID_SERVER_RESPONSE) throw new IllegalStateException( - "getNegotiatedProperty: authentication exchange not complete."); + "getNegotiatedProperty: authentication exchange not complete."); if (Sasl.QOP.equals(propName)) return "auth"; @@ -307,14 +281,12 @@ public Object getNegotiatedProperty( * SaslClient might be using. Invoking this method invalidates the * SaslClient instance. This method is idempotent. * - * @exception SaslException if a problem was encountered while disposing - * of the resources + * @throws SaslException if a problem was encountered while disposing + * of the resources */ public void dispose() - throws SaslException - { - if (m_state != STATE_DISPOSED) - { + throws SaslException { + if (m_state != STATE_DISPOSED) { m_state = STATE_DISPOSED; } } @@ -325,61 +297,53 @@ public void dispose() * method is called to prepare an appropriate next response to submit to * the server. * - * @param challenge The non-null challenge sent from the server. The - * challenge array may have zero length. - * - * @return The possibly null reponse to send to the server. It is null - * if the challenge accompanied a "SUCCESS" status and the - * challenge only contains data for the client to update its - * state and no response needs to be sent to the server. - * The response is a zero-length byte array if the client is to - * send a response with no data. - * - * @exception SaslException If an error occurred while processing the - * challenge or generating a response. + * @param challenge The non-null challenge sent from the server. The + * challenge array may have zero length. + * @return The possibly null reponse to send to the server. It is null + * if the challenge accompanied a "SUCCESS" status and the + * challenge only contains data for the client to update its + * state and no response needs to be sent to the server. + * The response is a zero-length byte array if the client is to + * send a response with no data. + * @throws SaslException If an error occurred while processing the + * challenge or generating a response. */ public byte[] evaluateChallenge( - byte[] challenge) - throws SaslException - { + byte[] challenge) + throws SaslException { byte[] response = null; //printState(); - switch (m_state) - { - case STATE_INITIAL: - if (challenge.length == 0) - throw new SaslException("response = byte[0]"); - else - try - { - response = createDigestResponse(challenge). - getBytes("UTF-8"); - m_state = STATE_DIGEST_RESPONSE_SENT; + switch (m_state) { + case STATE_INITIAL: + if (challenge.length == 0) + throw new SaslException("response = byte[0]"); + else + try { + response = createDigestResponse(challenge). + getBytes("UTF-8"); + m_state = STATE_DIGEST_RESPONSE_SENT; + } catch (java.io.UnsupportedEncodingException e) { + throw new SaslException( + "UTF-8 encoding not suppported by platform", e); + } + break; + case STATE_DIGEST_RESPONSE_SENT: + if (checkServerResponseAuth(challenge)) + m_state = STATE_VALID_SERVER_RESPONSE; + else { + m_state = STATE_INVALID_SERVER_RESPONSE; + throw new SaslException("Could not validate response-auth " + + "value from server"); } - catch (java.io.UnsupportedEncodingException e) - { - throw new SaslException( - "UTF-8 encoding not suppported by platform", e); - } - break; - case STATE_DIGEST_RESPONSE_SENT: - if (checkServerResponseAuth(challenge)) - m_state = STATE_VALID_SERVER_RESPONSE; - else - { - m_state = STATE_INVALID_SERVER_RESPONSE; - throw new SaslException("Could not validate response-auth " + - "value from server"); - } - break; - case STATE_VALID_SERVER_RESPONSE: - case STATE_INVALID_SERVER_RESPONSE: - throw new SaslException("Authentication sequence is complete"); - case STATE_DISPOSED: - throw new SaslException("Client has been disposed"); - default: - throw new SaslException("Unknown client state."); + break; + case STATE_VALID_SERVER_RESPONSE: + case STATE_INVALID_SERVER_RESPONSE: + throw new SaslException("Authentication sequence is complete"); + case STATE_DISPOSED: + throw new SaslException("Client has been disposed"); + default: + throw new SaslException("Unknown client state."); } return response; @@ -387,28 +351,25 @@ public byte[] evaluateChallenge( /** * This function takes a 16 byte binary md5-hash value and creates a 32 - * character (plus a terminating null character) hex-digit + * character (plus a terminating null character) hex-digit * representation of binary data. * - * @param hash 16 byte binary md5-hash value in bytes - * - * @return 32 character (plus a terminating null character) hex-digit - * representation of binary data. + * @param hash 16 byte binary md5-hash value in bytes + * @return 32 character (plus a terminating null character) hex-digit + * representation of binary data. */ char[] convertToHex( - byte[] hash) - { - int i; - byte j; - byte fifteen = 15; - char[] hex = new char[32]; - - for (i = 0; i < 16; i++) - { + byte[] hash) { + int i; + byte j; + byte fifteen = 15; + char[] hex = new char[32]; + + for (i = 0; i < 16; i++) { //convert value of top 4 bits to hex char - hex[i*2] = getHexChar((byte)((hash[i] & 0xf0) >> 4)); + hex[i * 2] = getHexChar((byte) ((hash[i] & 0xf0) >> 4)); //convert value of bottom 4 bits to hex char - hex[(i*2)+1] = getHexChar((byte)(hash[i] & 0x0f)); + hex[(i * 2) + 1] = getHexChar((byte) (hash[i] & 0x0f)); } return hex; @@ -417,29 +378,25 @@ char[] convertToHex( /** * Calculates the HA1 portion of the response * - * @param algorithm Algorith to use. - * @param userName User being authenticated - * @param realm realm information - * @param password password of teh user - * @param nonce nonce value - * @param clientNonce Clients Nonce value - * - * @return HA1 portion of the response in a character array - * - * @exception SaslException If an error occurs + * @param algorithm Algorith to use. + * @param userName User being authenticated + * @param realm realm information + * @param password password of teh user + * @param nonce nonce value + * @param clientNonce Clients Nonce value + * @return HA1 portion of the response in a character array + * @throws SaslException If an error occurs */ char[] DigestCalcHA1( - String algorithm, - String userName, - String realm, - String password, - String nonce, - String clientNonce) throws SaslException - { - byte[] hash; - - try - { + String algorithm, + String userName, + String realm, + String password, + String nonce, + String clientNonce) throws SaslException { + byte[] hash; + + try { MessageDigest md = MessageDigest.getInstance("MD5"); md.update(userName.getBytes("UTF-8")); @@ -449,8 +406,7 @@ char[] DigestCalcHA1( md.update(password.getBytes("UTF-8")); hash = md.digest(); - if ("md5-sess".equals(algorithm)) - { + if ("md5-sess".equals(algorithm)) { md.update(hash); md.update(":".getBytes("UTF-8")); md.update(nonce.getBytes("UTF-8")); @@ -458,15 +414,11 @@ char[] DigestCalcHA1( md.update(clientNonce.getBytes("UTF-8")); hash = md.digest(); } - } - catch(NoSuchAlgorithmException e) - { + } catch (NoSuchAlgorithmException e) { throw new SaslException("No provider found for MD5 hash", e); - } - catch(UnsupportedEncodingException e) - { + } catch (UnsupportedEncodingException e) { throw new SaslException( - "UTF-8 encoding not supported by platform.", e); + "UTF-8 encoding not supported by platform.", e); } return convertToHex(hash); @@ -477,44 +429,39 @@ char[] DigestCalcHA1( * This function calculates the response-value of the response directive of * the digest-response as documented in RFC 2831 * - * @param HA1 H(A1) - * @param serverNonce nonce from server - * @param nonceCount 8 hex digits - * @param clientNonce client nonce - * @param qop qop-value: "", "auth", "auth-int" - * @param method method from the request - * @param digestUri requested URL - * @param clientResponseFlag request-digest or response-digest - * + * @param HA1 H(A1) + * @param serverNonce nonce from server + * @param nonceCount 8 hex digits + * @param clientNonce client nonce + * @param qop qop-value: "", "auth", "auth-int" + * @param method method from the request + * @param digestUri requested URL + * @param clientResponseFlag request-digest or response-digest * @return Response-value of the response directive of the digest-response - * - * @exception SaslException If an error occurs + * @throws SaslException If an error occurs */ char[] DigestCalcResponse( - char[] HA1, /* H(A1) */ - String serverNonce, /* nonce from server */ - String nonceCount, /* 8 hex digits */ - String clientNonce, /* client nonce */ - String qop, /* qop-value: "", "auth", "auth-int" */ - String method, /* method from the request */ - String digestUri, /* requested URL */ - boolean clientResponseFlag) /* request-digest or response-digest */ - throws SaslException - { - byte[] HA2; - byte[] respHash; - char[] HA2Hex; + char[] HA1, /* H(A1) */ + String serverNonce, /* nonce from server */ + String nonceCount, /* 8 hex digits */ + String clientNonce, /* client nonce */ + String qop, /* qop-value: "", "auth", "auth-int" */ + String method, /* method from the request */ + String digestUri, /* requested URL */ + boolean clientResponseFlag) /* request-digest or response-digest */ + throws SaslException { + byte[] HA2; + byte[] respHash; + char[] HA2Hex; // calculate H(A2) - try - { + try { MessageDigest md = MessageDigest.getInstance("MD5"); if (clientResponseFlag) - md.update(method.getBytes("UTF-8")); + md.update(method.getBytes("UTF-8")); md.update(":".getBytes("UTF-8")); md.update(digestUri.getBytes("UTF-8")); - if ("auth-int".equals(qop)) - { + if ("auth-int".equals(qop)) { md.update(":".getBytes("UTF-8")); md.update("00000000000000000000000000000000".getBytes("UTF-8")); } @@ -526,8 +473,7 @@ char[] DigestCalcResponse( md.update(":".getBytes("UTF-8")); md.update(serverNonce.getBytes("UTF-8")); md.update(":".getBytes("UTF-8")); - if (qop.length() > 0) - { + if (qop.length() > 0) { md.update(nonceCount.getBytes("UTF-8")); md.update(":".getBytes("UTF-8")); md.update(clientNonce.getBytes("UTF-8")); @@ -537,15 +483,11 @@ char[] DigestCalcResponse( } md.update(new String(HA2Hex).getBytes("UTF-8")); respHash = md.digest(); - } - catch(NoSuchAlgorithmException e) - { + } catch (NoSuchAlgorithmException e) { throw new SaslException("No provider found for MD5 hash", e); - } - catch(UnsupportedEncodingException e) - { + } catch (UnsupportedEncodingException e) { throw new SaslException( - "UTF-8 encoding not supported by platform.", e); + "UTF-8 encoding not supported by platform.", e); } return convertToHex(respHash); @@ -555,24 +497,22 @@ char[] DigestCalcResponse( /** * Creates the intial response to be sent to the server. * - * @param challenge Challenge in bytes recived form the Server - * + * @param challenge Challenge in bytes recived form the Server * @return Initial response to be sent to the server */ private String createDigestResponse( - byte[] challenge) - throws SaslException - { - char[] response; - StringBuffer digestResponse = new StringBuffer(512); - int realmSize; + byte[] challenge) + throws SaslException { + char[] response; + StringBuffer digestResponse = new StringBuffer(512); + int realmSize; m_dc = new DigestChallenge(challenge); m_digestURI = m_protocol + "/" + m_serverName; if ((m_dc.getQop() & DigestChallenge.QOP_AUTH) - == DigestChallenge.QOP_AUTH ) + == DigestChallenge.QOP_AUTH) m_qopValue = "auth"; else throw new SaslException("Client only supports qop of 'auth'"); @@ -581,25 +521,20 @@ private String createDigestResponse( Callback[] callbacks = new Callback[3]; ArrayList realms = m_dc.getRealms(); realmSize = realms.size(); - if (realmSize == 0) - { + if (realmSize == 0) { callbacks[0] = new RealmCallback("Realm"); - } - else if (realmSize == 1) - { - callbacks[0] = new RealmCallback("Realm", (String)realms.get(0)); - } - else - { + } else if (realmSize == 1) { + callbacks[0] = new RealmCallback("Realm", (String) realms.get(0)); + } else { callbacks[0] = - new RealmChoiceCallback( - "Realm", - (String[])realms.toArray(new String[realmSize]), - 0, //the default choice index - false); //no multiple selections + new RealmChoiceCallback( + "Realm", + (String[]) realms.toArray(new String[realmSize]), + 0, //the default choice index + false); //no multiple selections } - callbacks[1] = new PasswordCallback("Password", false); + callbacks[1] = new PasswordCallback("Password", false); //false = no echo if (m_authorizationId == null || m_authorizationId.length() == 0) @@ -607,63 +542,55 @@ else if (realmSize == 1) else callbacks[2] = new NameCallback("Name", m_authorizationId); - try - { + try { m_cbh.handle(callbacks); - } - catch(UnsupportedCallbackException e) - { + } catch (UnsupportedCallbackException e) { throw new SaslException("Handler does not support" + - " necessary callbacks",e); - } - catch(IOException e) - { + " necessary callbacks", e); + } catch (IOException e) { throw new SaslException("IO exception in CallbackHandler.", e); } - if (realmSize > 1) - { + if (realmSize > 1) { int[] selections = - ((RealmChoiceCallback)callbacks[0]).getSelectedIndexes(); + ((RealmChoiceCallback) callbacks[0]).getSelectedIndexes(); if (selections.length > 0) m_realm = - ((RealmChoiceCallback)callbacks[0]).getChoices()[selections[0]]; + ((RealmChoiceCallback) callbacks[0]).getChoices()[selections[0]]; else - m_realm = ((RealmChoiceCallback)callbacks[0]).getChoices()[0]; - } - else - m_realm = ((RealmCallback)callbacks[0]).getText(); + m_realm = ((RealmChoiceCallback) callbacks[0]).getChoices()[0]; + } else + m_realm = ((RealmCallback) callbacks[0]).getText(); m_clientNonce = getClientNonce(); - m_name = ((NameCallback)callbacks[2]).getName(); + m_name = ((NameCallback) callbacks[2]).getName(); if (m_name == null) - m_name = ((NameCallback)callbacks[2]).getDefaultName(); + m_name = ((NameCallback) callbacks[2]).getDefaultName(); if (m_name == null) throw new SaslException("No user name was specified."); m_HA1 = DigestCalcHA1( - m_dc.getAlgorithm(), - m_name, - m_realm, - new String(((PasswordCallback)callbacks[1]).getPassword()), - m_dc.getNonce(), - m_clientNonce); + m_dc.getAlgorithm(), + m_name, + m_realm, + new String(((PasswordCallback) callbacks[1]).getPassword()), + m_dc.getNonce(), + m_clientNonce); response = DigestCalcResponse(m_HA1, - m_dc.getNonce(), - "00000001", - m_clientNonce, - m_qopValue, - "AUTHENTICATE", - m_digestURI, - true); + m_dc.getNonce(), + "00000001", + m_clientNonce, + m_qopValue, + "AUTHENTICATE", + m_digestURI, + true); digestResponse.append("username=\""); digestResponse.append(m_authorizationId); - if (0 != m_realm.length()) - { + if (0 != m_realm.length()) { digestResponse.append("\",realm=\""); digestResponse.append(m_realm); } @@ -674,7 +601,7 @@ else if (realmSize == 1) digestResponse.append(",qop="); digestResponse.append(m_qopValue); digestResponse.append(",digest-uri=\""); - digestResponse.append(m_digestURI); + digestResponse.append(m_digestURI); digestResponse.append("\",response="); digestResponse.append(response); digestResponse.append(",charset=utf-8,nonce=\""); @@ -682,38 +609,35 @@ else if (realmSize == 1) digestResponse.append("\""); return digestResponse.toString(); - } - - + } + + /** - * This function validates the server response. This step performs a + * This function validates the server response. This step performs a * modicum of mutual authentication by verifying that the server knows * the user's password * - * @param serverResponse Response recived form Server - * - * @return true if the mutual authentication succeeds; - * else return false - * - * @exception SaslException If an error occurs + * @param serverResponse Response recived form Server + * @return true if the mutual authentication succeeds; + * else return false + * @throws SaslException If an error occurs */ boolean checkServerResponseAuth( - byte[] serverResponse) throws SaslException - { - char[] response; - ResponseAuth responseAuth = null; - String responseStr; + byte[] serverResponse) throws SaslException { + char[] response; + ResponseAuth responseAuth = null; + String responseStr; responseAuth = new ResponseAuth(serverResponse); response = DigestCalcResponse(m_HA1, - m_dc.getNonce(), - "00000001", - m_clientNonce, - m_qopValue, - DIGEST_METHOD, - m_digestURI, - false); + m_dc.getNonce(), + "00000001", + m_clientNonce, + m_qopValue, + DIGEST_METHOD, + m_digestURI, + false); responseStr = new String(response); @@ -723,96 +647,86 @@ boolean checkServerResponseAuth( /** * This function returns hex character representing the value of the input - * - * @param value Input value in byte * + * @param value Input value in byte * @return Hex value of the Input byte value */ private static char getHexChar( - byte value) - { - switch (value) - { - case 0: - return '0'; - case 1: - return '1'; - case 2: - return '2'; - case 3: - return '3'; - case 4: - return '4'; - case 5: - return '5'; - case 6: - return '6'; - case 7: - return '7'; - case 8: - return '8'; - case 9: - return '9'; - case 10: - return 'a'; - case 11: - return 'b'; - case 12: - return 'c'; - case 13: - return 'd'; - case 14: - return 'e'; - case 15: - return 'f'; - default: - return 'Z'; + byte value) { + switch (value) { + case 0: + return '0'; + case 1: + return '1'; + case 2: + return '2'; + case 3: + return '3'; + case 4: + return '4'; + case 5: + return '5'; + case 6: + return '6'; + case 7: + return '7'; + case 8: + return '8'; + case 9: + return '9'; + case 10: + return 'a'; + case 11: + return 'b'; + case 12: + return 'c'; + case 13: + return 'd'; + case 14: + return 'e'; + case 15: + return 'f'; + default: + return 'Z'; } } /** * Calculates the Nonce value of the Client - * - * @return Nonce value of the client * - * @exception SaslException If an error Occurs + * @return Nonce value of the client + * @throws SaslException If an error Occurs */ - String getClientNonce() throws SaslException - { - byte[] nonceBytes = new byte[NONCE_BYTE_COUNT]; - SecureRandom prng; - byte nonceByte; - char[] hexNonce = new char[NONCE_HEX_COUNT]; - - try - { + String getClientNonce() throws SaslException { + byte[] nonceBytes = new byte[NONCE_BYTE_COUNT]; + SecureRandom prng; + byte nonceByte; + char[] hexNonce = new char[NONCE_HEX_COUNT]; + + try { prng = SecureRandom.getInstance("SHA1PRNG"); prng.nextBytes(nonceBytes); - for(int i=0; i> 4)); + hexNonce[(i * 2) + 1] = getHexChar((byte) ((nonceBytes[i] & 0xf0) + >> 4)); } return new String(hexNonce); - } - catch(NoSuchAlgorithmException e) - { + } catch (NoSuchAlgorithmException e) { throw new SaslException("No random number generator available", e); } } /** * Returns the IANA-registered mechanism name of this SASL client. - * (e.g. "CRAM-MD5", "GSSAPI") + * (e.g. "CRAM-MD5", "GSSAPI") * - * @return "DIGEST-MD5"the IANA-registered mechanism name of this SASL - * client. + * @return "DIGEST-MD5"the IANA-registered mechanism name of this SASL + * client. */ - public String getMechanismName() - { + public String getMechanismName() { return "DIGEST-MD5"; } diff --git a/app/src/main/java/com/novell/sasl/client/DirectiveList.java b/app/src/main/java/com/novell/sasl/client/DirectiveList.java index fc26a6b0ee..b3d710ecfb 100644 --- a/app/src/main/java/com/novell/sasl/client/DirectiveList.java +++ b/app/src/main/java/com/novell/sasl/client/DirectiveList.java @@ -15,50 +15,47 @@ package com.novell.sasl.client; import java.util.*; + import org.apache.harmony.javax.security.sasl.*; + import java.io.UnsupportedEncodingException; /** - * Implements the DirectiveList class whihc will be used by the + * Implements the DirectiveList class whihc will be used by the * DigestMD5SaslClient class */ -class DirectiveList extends Object -{ - private static final int STATE_LOOKING_FOR_FIRST_DIRECTIVE = 1; - private static final int STATE_LOOKING_FOR_DIRECTIVE = 2; - private static final int STATE_SCANNING_NAME = 3; - private static final int STATE_LOOKING_FOR_EQUALS = 4; - private static final int STATE_LOOKING_FOR_VALUE = 5; - private static final int STATE_LOOKING_FOR_COMMA = 6; - private static final int STATE_SCANNING_QUOTED_STRING_VALUE = 7; - private static final int STATE_SCANNING_TOKEN_VALUE = 8; - private static final int STATE_NO_UTF8_SUPPORT = 9; - - private int m_curPos; - private int m_errorPos; - private String m_directives; - private int m_state; - private ArrayList m_directiveList; - private String m_curName; - private int m_scanStart; +class DirectiveList extends Object { + private static final int STATE_LOOKING_FOR_FIRST_DIRECTIVE = 1; + private static final int STATE_LOOKING_FOR_DIRECTIVE = 2; + private static final int STATE_SCANNING_NAME = 3; + private static final int STATE_LOOKING_FOR_EQUALS = 4; + private static final int STATE_LOOKING_FOR_VALUE = 5; + private static final int STATE_LOOKING_FOR_COMMA = 6; + private static final int STATE_SCANNING_QUOTED_STRING_VALUE = 7; + private static final int STATE_SCANNING_TOKEN_VALUE = 8; + private static final int STATE_NO_UTF8_SUPPORT = 9; + + private int m_curPos; + private int m_errorPos; + private String m_directives; + private int m_state; + private ArrayList m_directiveList; + private String m_curName; + private int m_scanStart; /** - * Constructs a new DirectiveList. + * Constructs a new DirectiveList. */ - DirectiveList( - byte[] directives) - { + DirectiveList( + byte[] directives) { m_curPos = 0; m_state = STATE_LOOKING_FOR_FIRST_DIRECTIVE; m_directiveList = new ArrayList(10); m_scanStart = 0; m_errorPos = -1; - try - { + try { m_directives = new String(directives, "UTF-8"); - } - catch(UnsupportedEncodingException e) - { + } catch (UnsupportedEncodingException e) { m_state = STATE_NO_UTF8_SUPPORT; } } @@ -70,149 +67,113 @@ class DirectiveList extends Object * name followed by an equal sign (=) and the directive value. The value is * either a token or a quoted string * - * @exception SaslException If an error Occurs + * @throws SaslException If an error Occurs */ - void parseDirectives() throws SaslException - { - char prevChar; - char currChar; - int rc = 0; - boolean haveQuotedPair = false; - String currentName = ""; + void parseDirectives() throws SaslException { + char prevChar; + char currChar; + int rc = 0; + boolean haveQuotedPair = false; + String currentName = ""; if (m_state == STATE_NO_UTF8_SUPPORT) throw new SaslException("No UTF-8 support on platform"); prevChar = 0; - while (m_curPos < m_directives.length()) - { + while (m_curPos < m_directives.length()) { currChar = m_directives.charAt(m_curPos); - switch (m_state) - { - case STATE_LOOKING_FOR_FIRST_DIRECTIVE: - case STATE_LOOKING_FOR_DIRECTIVE: - if (isWhiteSpace(currChar)) - { + switch (m_state) { + case STATE_LOOKING_FOR_FIRST_DIRECTIVE: + case STATE_LOOKING_FOR_DIRECTIVE: + if (isWhiteSpace(currChar)) { + break; + } else if (isValidTokenChar(currChar)) { + m_scanStart = m_curPos; + m_state = STATE_SCANNING_NAME; + } else { + m_errorPos = m_curPos; + throw new SaslException("Parse error: Invalid name character"); + } break; - } - else if (isValidTokenChar(currChar)) - { - m_scanStart = m_curPos; - m_state = STATE_SCANNING_NAME; - } - else - { - m_errorPos = m_curPos; - throw new SaslException("Parse error: Invalid name character"); - } - break; - case STATE_SCANNING_NAME: - if (isValidTokenChar(currChar)) - { + case STATE_SCANNING_NAME: + if (isValidTokenChar(currChar)) { + break; + } else if (isWhiteSpace(currChar)) { + currentName = m_directives.substring(m_scanStart, m_curPos); + m_state = STATE_LOOKING_FOR_EQUALS; + } else if ('=' == currChar) { + currentName = m_directives.substring(m_scanStart, m_curPos); + m_state = STATE_LOOKING_FOR_VALUE; + } else { + m_errorPos = m_curPos; + throw new SaslException("Parse error: Invalid name character"); + } break; - } - else if (isWhiteSpace(currChar)) - { - currentName = m_directives.substring(m_scanStart, m_curPos); - m_state = STATE_LOOKING_FOR_EQUALS; - } - else if ('=' == currChar) - { - currentName = m_directives.substring(m_scanStart, m_curPos); - m_state = STATE_LOOKING_FOR_VALUE; - } - else - { - m_errorPos = m_curPos; - throw new SaslException("Parse error: Invalid name character"); - } - break; - case STATE_LOOKING_FOR_EQUALS: - if (isWhiteSpace(currChar)) - { + case STATE_LOOKING_FOR_EQUALS: + if (isWhiteSpace(currChar)) { + break; + } else if ('=' == currChar) { + m_state = STATE_LOOKING_FOR_VALUE; + } else { + m_errorPos = m_curPos; + throw new SaslException("Parse error: Expected equals sign '='."); + } break; - } - else if ('=' == currChar) - { - m_state = STATE_LOOKING_FOR_VALUE; - } - else - { - m_errorPos = m_curPos; - throw new SaslException("Parse error: Expected equals sign '='."); - } - break; - case STATE_LOOKING_FOR_VALUE: - if (isWhiteSpace(currChar)) - { + case STATE_LOOKING_FOR_VALUE: + if (isWhiteSpace(currChar)) { + break; + } else if ('"' == currChar) { + m_scanStart = m_curPos + 1; /* don't include the quote */ + m_state = STATE_SCANNING_QUOTED_STRING_VALUE; + } else if (isValidTokenChar(currChar)) { + m_scanStart = m_curPos; + m_state = STATE_SCANNING_TOKEN_VALUE; + } else { + m_errorPos = m_curPos; + throw new SaslException("Parse error: Unexpected character"); + } break; - } - else if ('"' == currChar) - { - m_scanStart = m_curPos+1; /* don't include the quote */ - m_state = STATE_SCANNING_QUOTED_STRING_VALUE; - } - else if (isValidTokenChar(currChar)) - { - m_scanStart = m_curPos; - m_state = STATE_SCANNING_TOKEN_VALUE; - } - else - { - m_errorPos = m_curPos; - throw new SaslException("Parse error: Unexpected character"); - } - break; - case STATE_SCANNING_TOKEN_VALUE: - if (isValidTokenChar(currChar)) - { + case STATE_SCANNING_TOKEN_VALUE: + if (isValidTokenChar(currChar)) { + break; + } else if (isWhiteSpace(currChar)) { + addDirective(currentName, false); + m_state = STATE_LOOKING_FOR_COMMA; + } else if (',' == currChar) { + addDirective(currentName, false); + m_state = STATE_LOOKING_FOR_DIRECTIVE; + } else { + m_errorPos = m_curPos; + throw new SaslException("Parse error: Invalid value character"); + } break; - } - else if (isWhiteSpace(currChar)) - { - addDirective(currentName, false); - m_state = STATE_LOOKING_FOR_COMMA; - } - else if (',' == currChar) - { - addDirective(currentName, false); - m_state = STATE_LOOKING_FOR_DIRECTIVE; - } - else - { - m_errorPos = m_curPos; - throw new SaslException("Parse error: Invalid value character"); - } - break; - case STATE_SCANNING_QUOTED_STRING_VALUE: - if ('\\' == currChar) - haveQuotedPair = true; - if ( ('"' == currChar) && - ('\\' != prevChar) ) - { - addDirective(currentName, haveQuotedPair); - haveQuotedPair = false; - m_state = STATE_LOOKING_FOR_COMMA; - } - break; + case STATE_SCANNING_QUOTED_STRING_VALUE: + if ('\\' == currChar) + haveQuotedPair = true; + if (('"' == currChar) && + ('\\' != prevChar)) { + addDirective(currentName, haveQuotedPair); + haveQuotedPair = false; + m_state = STATE_LOOKING_FOR_COMMA; + } + break; - case STATE_LOOKING_FOR_COMMA: - if (isWhiteSpace(currChar)) + case STATE_LOOKING_FOR_COMMA: + if (isWhiteSpace(currChar)) + break; + else if (currChar == ',') + m_state = STATE_LOOKING_FOR_DIRECTIVE; + else { + m_errorPos = m_curPos; + throw new SaslException("Parse error: Expected a comma."); + } break; - else if (currChar == ',') - m_state = STATE_LOOKING_FOR_DIRECTIVE; - else - { - m_errorPos = m_curPos; - throw new SaslException("Parse error: Expected a comma."); - } - break; } if (0 != rc) break; @@ -221,28 +182,26 @@ else if (currChar == ',') } /* end while loop */ - if (rc == 0) - { + if (rc == 0) { /* check the ending state */ - switch (m_state) - { - case STATE_SCANNING_TOKEN_VALUE: - addDirective(currentName, false); - break; + switch (m_state) { + case STATE_SCANNING_TOKEN_VALUE: + addDirective(currentName, false); + break; - case STATE_LOOKING_FOR_FIRST_DIRECTIVE: - case STATE_LOOKING_FOR_COMMA: - break; + case STATE_LOOKING_FOR_FIRST_DIRECTIVE: + case STATE_LOOKING_FOR_COMMA: + break; - case STATE_LOOKING_FOR_DIRECTIVE: + case STATE_LOOKING_FOR_DIRECTIVE: throw new SaslException("Parse error: Trailing comma."); - case STATE_SCANNING_NAME: - case STATE_LOOKING_FOR_EQUALS: - case STATE_LOOKING_FOR_VALUE: + case STATE_SCANNING_NAME: + case STATE_LOOKING_FOR_EQUALS: + case STATE_LOOKING_FOR_VALUE: throw new SaslException("Parse error: Missing value."); - case STATE_SCANNING_QUOTED_STRING_VALUE: + case STATE_SCANNING_QUOTED_STRING_VALUE: throw new SaslException("Parse error: Missing closing quote."); } } @@ -251,36 +210,34 @@ else if (currChar == ',') /** * This function returns TRUE if the character is a valid token character. + *

+ * token = 1* + *

+ * separators = "(" | ")" | "<" | ">" | "@" + * | "," | ";" | ":" | "\" | <"> + * | "/" | "[" | "]" | "?" | "=" + * | "{" | "}" | SP | HT + *

+ * CTL = + *

+ * CHAR = * - * token = 1* - * - * separators = "(" | ")" | "<" | ">" | "@" - * | "," | ";" | ":" | "\" | <"> - * | "/" | "[" | "]" | "?" | "=" - * | "{" | "}" | SP | HT - * - * CTL = - * - * CHAR = - * - * @param c character to be tested - * + * @param c character to be tested * @return Returns TRUE if the character is a valid token character. */ boolean isValidTokenChar( - char c) - { - if ( ( (c >= '\u0000') && (c <='\u0020') ) || - ( (c >= '\u003a') && (c <= '\u0040') ) || - ( (c >= '\u005b') && (c <= '\u005d') ) || - ('\u002c' == c) || - ('\u0025' == c) || - ('\u0028' == c) || - ('\u0029' == c) || - ('\u007b' == c) || - ('\u007d' == c) || - ('\u007f' == c) ) + char c) { + if (((c >= '\u0000') && (c <= '\u0020')) || + ((c >= '\u003a') && (c <= '\u0040')) || + ((c >= '\u005b') && (c <= '\u005d')) || + ('\u002c' == c) || + ('\u0025' == c) || + ('\u0028' == c) || + ('\u0029' == c) || + ('\u007b' == c) || + ('\u007d' == c) || + ('\u007f' == c)) return false; return true; @@ -288,18 +245,17 @@ boolean isValidTokenChar( /** * This function returns TRUE if the character is linear white space (LWS). - * LWS = [CRLF] 1*( SP | HT ) - * @param c Input charcter to be tested + * LWS = [CRLF] 1*( SP | HT ) * + * @param c Input charcter to be tested * @return Returns TRUE if the character is linear white space (LWS) */ boolean isWhiteSpace( - char c) - { - if ( ('\t' == c) || // HORIZONTAL TABULATION. - ('\n' == c) || // LINE FEED. - ('\r' == c) || // CARRIAGE RETURN. - ('\u0020' == c) ) + char c) { + if (('\t' == c) || // HORIZONTAL TABULATION. + ('\n' == c) || // LINE FEED. + ('\r' == c) || // CARRIAGE RETURN. + ('\u0020' == c)) return true; return false; @@ -309,30 +265,25 @@ boolean isWhiteSpace( * This function creates a directive record and adds it to the list, the * value will be added later after it is parsed. * - * @param name Name + * @param name Name * @param haveQuotedPair true if quoted pair is there else false */ void addDirective( - String name, - boolean haveQuotedPair) - { + String name, + boolean haveQuotedPair) { String value; - int inputIndex; - int valueIndex; - char valueChar; - int type; + int inputIndex; + int valueIndex; + char valueChar; + int type; - if (!haveQuotedPair) - { + if (!haveQuotedPair) { value = m_directives.substring(m_scanStart, m_curPos); - } - else - { //copy one character at a time skipping backslash excapes. + } else { //copy one character at a time skipping backslash excapes. StringBuffer valueBuf = new StringBuffer(m_curPos - m_scanStart); valueIndex = 0; inputIndex = m_scanStart; - while (inputIndex < m_curPos) - { + while (inputIndex < m_curPos) { if ('\\' == (valueChar = m_directives.charAt(inputIndex))) inputIndex++; valueBuf.setCharAt(valueIndex, m_directives.charAt(inputIndex)); @@ -353,10 +304,9 @@ void addDirective( /** * Returns the List iterator. * - * @return Returns the Iterator Object for the List. + * @return Returns the Iterator Object for the List. */ - Iterator getIterator() - { + Iterator getIterator() { return m_directiveList.iterator(); } } diff --git a/app/src/main/java/com/novell/sasl/client/ParsedDirective.java b/app/src/main/java/com/novell/sasl/client/ParsedDirective.java index 17bf70e04a..921a99bc61 100644 --- a/app/src/main/java/com/novell/sasl/client/ParsedDirective.java +++ b/app/src/main/java/com/novell/sasl/client/ParsedDirective.java @@ -18,37 +18,32 @@ * Implements the ParsedDirective class which will be used in the * DigestMD5SaslClient mechanism. */ -class ParsedDirective -{ - public static final int QUOTED_STRING_VALUE = 1; - public static final int TOKEN_VALUE = 2; +class ParsedDirective { + public static final int QUOTED_STRING_VALUE = 1; + public static final int TOKEN_VALUE = 2; - private int m_valueType; - private String m_name; - private String m_value; + private int m_valueType; + private String m_name; + private String m_value; ParsedDirective( - String name, - String value, - int type) - { + String name, + String value, + int type) { m_name = name; m_value = value; m_valueType = type; } - String getValue() - { + String getValue() { return m_value; } - String getName() - { + String getName() { return m_name; } - int getValueType() - { + int getValueType() { return m_valueType; } diff --git a/app/src/main/java/com/novell/sasl/client/ResponseAuth.java b/app/src/main/java/com/novell/sasl/client/ResponseAuth.java index 0aef955d05..885b32fd50 100644 --- a/app/src/main/java/com/novell/sasl/client/ResponseAuth.java +++ b/app/src/main/java/com/novell/sasl/client/ResponseAuth.java @@ -15,30 +15,26 @@ package com.novell.sasl.client; import java.util.*; + import org.apache.harmony.javax.security.sasl.*; /** * Implements the ResponseAuth class used by the DigestMD5SaslClient mechanism */ -class ResponseAuth -{ +class ResponseAuth { private String m_responseValue; ResponseAuth( - byte[] responseAuth) - throws SaslException - { + byte[] responseAuth) + throws SaslException { m_responseValue = null; DirectiveList dirList = new DirectiveList(responseAuth); - try - { + try { dirList.parseDirectives(); checkSemantics(dirList); - } - catch (SaslException e) - { + } catch (SaslException e) { } } @@ -46,20 +42,17 @@ class ResponseAuth * Checks the semantics of the directives in the directive list as parsed * from the digest challenge byte array. * - * @param dirList the list of directives parsed from the digest challenge - * - * @exception SaslException If a semantic error occurs + * @param dirList the list of directives parsed from the digest challenge + * @throws SaslException If a semantic error occurs */ void checkSemantics( - DirectiveList dirList) throws SaslException - { - Iterator directives = dirList.getIterator(); + DirectiveList dirList) throws SaslException { + Iterator directives = dirList.getIterator(); ParsedDirective directive; - String name; + String name; - while (directives.hasNext()) - { - directive = (ParsedDirective)directives.next(); + while (directives.hasNext()) { + directive = (ParsedDirective) directives.next(); name = directive.getName(); if (name.equals("rspauth")) m_responseValue = directive.getValue(); @@ -75,8 +68,7 @@ void checkSemantics( * * @return the ResponseValue as a String. */ - public String getResponseValue() - { + public String getResponseValue() { return m_responseValue; } } diff --git a/app/src/main/java/com/novell/sasl/client/TokenParser.java b/app/src/main/java/com/novell/sasl/client/TokenParser.java index 3d3491de92..d2711cd87a 100644 --- a/app/src/main/java/com/novell/sasl/client/TokenParser.java +++ b/app/src/main/java/com/novell/sasl/client/TokenParser.java @@ -15,131 +15,111 @@ package com.novell.sasl.client; import org.apache.harmony.javax.security.sasl.*; + /** * The TokenParser class will parse individual tokens from a list of tokens that * are a directive value for a DigestMD5 authentication.The tokens are separated * commas. */ -class TokenParser extends Object -{ +class TokenParser extends Object { private static final int STATE_LOOKING_FOR_FIRST_TOKEN = 1; - private static final int STATE_LOOKING_FOR_TOKEN = 2; - private static final int STATE_SCANNING_TOKEN = 3; - private static final int STATE_LOOKING_FOR_COMMA = 4; - private static final int STATE_PARSING_ERROR = 5; - private static final int STATE_DONE = 6; + private static final int STATE_LOOKING_FOR_TOKEN = 2; + private static final int STATE_SCANNING_TOKEN = 3; + private static final int STATE_LOOKING_FOR_COMMA = 4; + private static final int STATE_PARSING_ERROR = 5; + private static final int STATE_DONE = 6; - private int m_curPos; - private int m_scanStart; - private int m_state; - private String m_tokens; + private int m_curPos; + private int m_scanStart; + private int m_state; + private String m_tokens; TokenParser( - String tokens) - { + String tokens) { m_tokens = tokens; m_curPos = 0; m_scanStart = 0; - m_state = STATE_LOOKING_FOR_FIRST_TOKEN; + m_state = STATE_LOOKING_FOR_FIRST_TOKEN; } /** * This function parses the next token from the tokens string and returns * it as a string. If there are no more tokens a null reference is returned. * - * @return the parsed token or a null reference if there are no more + * @return the parsed token or a null reference if there are no more * tokens - * - * @exception SASLException if an error occurs while parsing + * @throws SASLException if an error occurs while parsing */ - String parseToken() throws SaslException - { - char currChar; - String token = null; + String parseToken() throws SaslException { + char currChar; + String token = null; if (m_state == STATE_DONE) return null; - while (m_curPos < m_tokens.length() && (token == null)) - { + while (m_curPos < m_tokens.length() && (token == null)) { currChar = m_tokens.charAt(m_curPos); - switch (m_state) - { - case STATE_LOOKING_FOR_FIRST_TOKEN: - case STATE_LOOKING_FOR_TOKEN: - if (isWhiteSpace(currChar)) - { + switch (m_state) { + case STATE_LOOKING_FOR_FIRST_TOKEN: + case STATE_LOOKING_FOR_TOKEN: + if (isWhiteSpace(currChar)) { + break; + } else if (isValidTokenChar(currChar)) { + m_scanStart = m_curPos; + m_state = STATE_SCANNING_TOKEN; + } else { + m_state = STATE_PARSING_ERROR; + throw new SaslException("Invalid token character at position " + m_curPos); + } break; - } - else if (isValidTokenChar(currChar)) - { - m_scanStart = m_curPos; - m_state = STATE_SCANNING_TOKEN; - } - else - { - m_state = STATE_PARSING_ERROR; - throw new SaslException("Invalid token character at position " + m_curPos); - } - break; - - case STATE_SCANNING_TOKEN: - if (isValidTokenChar(currChar)) - { + + case STATE_SCANNING_TOKEN: + if (isValidTokenChar(currChar)) { + break; + } else if (isWhiteSpace(currChar)) { + token = m_tokens.substring(m_scanStart, m_curPos); + m_state = STATE_LOOKING_FOR_COMMA; + } else if (',' == currChar) { + token = m_tokens.substring(m_scanStart, m_curPos); + m_state = STATE_LOOKING_FOR_TOKEN; + } else { + m_state = STATE_PARSING_ERROR; + throw new SaslException("Invalid token character at position " + m_curPos); + } break; - } - else if (isWhiteSpace(currChar)) - { - token = m_tokens.substring(m_scanStart, m_curPos); - m_state = STATE_LOOKING_FOR_COMMA; - } - else if (',' == currChar) - { - token = m_tokens.substring(m_scanStart, m_curPos); - m_state = STATE_LOOKING_FOR_TOKEN; - } - else - { - m_state = STATE_PARSING_ERROR; - throw new SaslException("Invalid token character at position " + m_curPos); - } - break; - - - case STATE_LOOKING_FOR_COMMA: - if (isWhiteSpace(currChar)) + + + case STATE_LOOKING_FOR_COMMA: + if (isWhiteSpace(currChar)) + break; + else if (currChar == ',') + m_state = STATE_LOOKING_FOR_TOKEN; + else { + m_state = STATE_PARSING_ERROR; + throw new SaslException("Expected a comma, found '" + + currChar + "' at postion " + + m_curPos); + } break; - else if (currChar == ',') - m_state = STATE_LOOKING_FOR_TOKEN; - else - { - m_state = STATE_PARSING_ERROR; - throw new SaslException("Expected a comma, found '" + - currChar + "' at postion " + - m_curPos); - } - break; } m_curPos++; } /* end while loop */ - if (token == null) - { /* check the ending state */ - switch (m_state) - { - case STATE_SCANNING_TOKEN: - token = m_tokens.substring(m_scanStart); - m_state = STATE_DONE; - break; - - case STATE_LOOKING_FOR_FIRST_TOKEN: - case STATE_LOOKING_FOR_COMMA: - break; - - case STATE_LOOKING_FOR_TOKEN: - throw new SaslException("Trialing comma"); + if (token == null) { /* check the ending state */ + switch (m_state) { + case STATE_SCANNING_TOKEN: + token = m_tokens.substring(m_scanStart); + m_state = STATE_DONE; + break; + + case STATE_LOOKING_FOR_FIRST_TOKEN: + case STATE_LOOKING_FOR_COMMA: + break; + + case STATE_LOOKING_FOR_TOKEN: + throw new SaslException("Trialing comma"); } } @@ -148,37 +128,35 @@ else if (currChar == ',') /** * This function returns TRUE if the character is a valid token character. + *

+ * token = 1* + *

+ * separators = "(" | ")" | "<" | ">" | "@" + * | "," | ";" | ":" | "\" | <"> + * | "/" | "[" | "]" | "?" | "=" + * | "{" | "}" | SP | HT + *

+ * CTL = + *

+ * CHAR = * - * token = 1* - * - * separators = "(" | ")" | "<" | ">" | "@" - * | "," | ";" | ":" | "\" | <"> - * | "/" | "[" | "]" | "?" | "=" - * | "{" | "}" | SP | HT - * - * CTL = - * - * CHAR = - * - * @param c character to be validated - * - * @return True if character is valid Token character else it returns + * @param c character to be validated + * @return True if character is valid Token character else it returns * false */ boolean isValidTokenChar( - char c) - { - if ( ( (c >= '\u0000') && (c <='\u0020') ) || - ( (c >= '\u003a') && (c <= '\u0040') ) || - ( (c >= '\u005b') && (c <= '\u005d') ) || - ('\u002c' == c) || - ('\u0025' == c) || - ('\u0028' == c) || - ('\u0029' == c) || - ('\u007b' == c) || - ('\u007d' == c) || - ('\u007f' == c) ) + char c) { + if (((c >= '\u0000') && (c <= '\u0020')) || + ((c >= '\u003a') && (c <= '\u0040')) || + ((c >= '\u005b') && (c <= '\u005d')) || + ('\u002c' == c) || + ('\u0025' == c) || + ('\u0028' == c) || + ('\u0029' == c) || + ('\u007b' == c) || + ('\u007d' == c) || + ('\u007f' == c)) return false; return true; @@ -186,19 +164,17 @@ boolean isValidTokenChar( /** * This function returns TRUE if the character is linear white space (LWS). - * LWS = [CRLF] 1*( SP | HT ) - * - * @param c character to be validated + * LWS = [CRLF] 1*( SP | HT ) * + * @param c character to be validated * @return True if character is liner whitespace else it returns false */ boolean isWhiteSpace( - char c) - { - if ( ('\t' == c) || // HORIZONTAL TABULATION. - ('\n' == c) || // LINE FEED. - ('\r' == c) || // CARRIAGE RETURN. - ('\u0020' == c) ) + char c) { + if (('\t' == c) || // HORIZONTAL TABULATION. + ('\n' == c) || // LINE FEED. + ('\r' == c) || // CARRIAGE RETURN. + ('\u0020' == c)) return true; return false; diff --git a/app/src/main/java/com/xabber/android/data/AbstractTable.java b/app/src/main/java/com/xabber/android/data/AbstractTable.java index f507443d11..c082f12f25 100644 --- a/app/src/main/java/com/xabber/android/data/AbstractTable.java +++ b/app/src/main/java/com/xabber/android/data/AbstractTable.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -19,39 +19,38 @@ /** * Abstract database table. - * + * * @author alexander.ivanov - * */ public abstract class AbstractTable implements DatabaseTable { - protected abstract String getTableName(); - - protected abstract String[] getProjection(); - - protected String getListOrder() { - return null; - } - - @Override - public void migrate(SQLiteDatabase db, int toVersion) { - } - - /** - * Query table. - * - * @return Result set with defined projection and in defined order. - */ - public Cursor list() { - SQLiteDatabase db = DatabaseManager.getInstance().getReadableDatabase(); - return db.query(getTableName(), getProjection(), null, null, null, - null, getListOrder()); - } - - @Override - public void clear() { - SQLiteDatabase db = DatabaseManager.getInstance().getWritableDatabase(); - db.delete(getTableName(), null, null); - } + protected abstract String getTableName(); + + protected abstract String[] getProjection(); + + protected String getListOrder() { + return null; + } + + @Override + public void migrate(SQLiteDatabase db, int toVersion) { + } + + /** + * Query table. + * + * @return Result set with defined projection and in defined order. + */ + public Cursor list() { + SQLiteDatabase db = DatabaseManager.getInstance().getReadableDatabase(); + return db.query(getTableName(), getProjection(), null, null, null, + null, getListOrder()); + } + + @Override + public void clear() { + SQLiteDatabase db = DatabaseManager.getInstance().getWritableDatabase(); + db.delete(getTableName(), null, null); + } } diff --git a/app/src/main/java/com/xabber/android/data/ActivityManager.java b/app/src/main/java/com/xabber/android/data/ActivityManager.java index 0871f70235..017923c907 100644 --- a/app/src/main/java/com/xabber/android/data/ActivityManager.java +++ b/app/src/main/java/com/xabber/android/data/ActivityManager.java @@ -1,322 +1,295 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.Map.Entry; -import java.util.WeakHashMap; - import android.app.Activity; import android.content.Context; import android.content.Intent; -import android.content.res.TypedArray; import android.os.Bundle; import android.widget.Toast; -import com.xabber.android.data.SettingsManager.InterfaceTheme; +import com.xabber.android.R; import com.xabber.android.ui.ContactList; import com.xabber.android.ui.LoadActivity; -import com.xabber.android.ui.PreferenceEditor; -import com.xabber.androiddev.R; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.WeakHashMap; /** * Activity stack manager. - * + * * @author alexander.ivanov - * */ public class ActivityManager implements OnUnloadListener { - private static final String EXTRA_TASK_INDEX = "com.xabber.android.data.ActivityManager.EXTRA_TASK_INDEX"; - - private static final boolean LOG = true; - - private final Application application; - - /** - * List of launched activities. - */ - private final ArrayList activities; - - /** - * Next index of task. - */ - private int nextTaskIndex; - - /** - * Activity with index of it task. - */ - private final WeakHashMap taskIndexes; + private static final String EXTRA_TASK_INDEX = "com.xabber.android.data.ActivityManager.EXTRA_TASK_INDEX"; - /** - * Listener for errors. - */ - private OnErrorListener onErrorListener; + private static final boolean LOG = true; + private final static ActivityManager instance; - private final static ActivityManager instance; + static { + instance = new ActivityManager(); + Application.getInstance().addManager(instance); + } - static { - instance = new ActivityManager(); - Application.getInstance().addManager(instance); - } + private final Application application; + /** + * List of launched activities. + */ + private final ArrayList activities; + /** + * Activity with index of it task. + */ + private final WeakHashMap taskIndexes; + /** + * Next index of task. + */ + private int nextTaskIndex; + /** + * Listener for errors. + */ + private OnErrorListener onErrorListener; - public static ActivityManager getInstance() { - return instance; - } + private ActivityManager() { + this.application = Application.getInstance(); + activities = new ArrayList(); + nextTaskIndex = 0; + taskIndexes = new WeakHashMap(); + } - private ActivityManager() { - this.application = Application.getInstance(); - activities = new ArrayList(); - nextTaskIndex = 0; - taskIndexes = new WeakHashMap(); - } + public static ActivityManager getInstance() { + return instance; + } - /** - * Removes finished activities from stask. - */ - private void rebuildStack() { - Iterator iterator = activities.iterator(); - while (iterator.hasNext()) - if (iterator.next().isFinishing()) - iterator.remove(); - } + /** + * Removes finished activities from stask. + */ + private void rebuildStack() { + Iterator iterator = activities.iterator(); + while (iterator.hasNext()) + if (iterator.next().isFinishing()) + iterator.remove(); + } - /** - * Finish all activities in stack till the root contact list. - * - * @param finishRoot - * also finish root contact list. - */ - public void clearStack(boolean finishRoot) { - ContactList root = null; - rebuildStack(); - for (Activity activity : activities) { - if (!finishRoot && root == null && activity instanceof ContactList) - root = (ContactList) activity; - else - activity.finish(); - } - rebuildStack(); - } + /** + * Finish all activities in stack till the root contact list. + * + * @param finishRoot also finish root contact list. + */ + public void clearStack(boolean finishRoot) { + ContactList root = null; + rebuildStack(); + for (Activity activity : activities) { + if (!finishRoot && root == null && activity instanceof ContactList) + root = (ContactList) activity; + else + activity.finish(); + } + rebuildStack(); + } - /** - * @return Whether contact list is in the activity stack. - */ - public boolean hasContactList(Context context) { - rebuildStack(); - for (Activity activity : activities) - if (activity instanceof ContactList) - return true; - return false; - } + /** + * @return Whether contact list is in the activity stack. + */ + public boolean hasContactList(Context context) { + rebuildStack(); + for (Activity activity : activities) + if (activity instanceof ContactList) + return true; + return false; + } - /** - * Apply theme settings. - * - * @param activity - */ - private void applyTheme(Activity activity) { - if (activity instanceof PreferenceEditor) - return; - TypedArray title = activity.getTheme().obtainStyledAttributes( - new int[] { android.R.attr.windowNoTitle, - android.R.attr.windowIsFloating }); - boolean noTitle = title.getBoolean(0, false); - boolean isFloating = title.getBoolean(1, false); - title.recycle(); - if (isFloating) - return; - InterfaceTheme theme = SettingsManager.interfaceTheme(); - if (theme == SettingsManager.InterfaceTheme.light) - activity.setTheme(noTitle ? R.style.Theme_Light_NoTitleBar - : R.style.Theme_Light); - else if (theme == SettingsManager.InterfaceTheme.dark) - activity.setTheme(noTitle ? R.style.Theme_Dark_NoTitleBar - : R.style.Theme_Dark); - } + /** + * Apply theme settings. + * + * @param activity + */ + private void applyTheme(Activity activity) { + activity.setTheme(R.style.Theme); + } - /** - * Push activity to stack. - * - * Must be called from {@link Activity#onCreate(Bundle)}. - * - * @param activity - */ - public void onCreate(Activity activity) { - if (LOG) - LogManager.i(activity, "onCreate: " + activity.getIntent()); - applyTheme(activity); - if (application.isClosing() && !(activity instanceof LoadActivity)) { - activity.startActivity(LoadActivity.createIntent(activity)); - activity.finish(); - } - activities.add(activity); - rebuildStack(); - fetchTaskIndex(activity, activity.getIntent()); - } + /** + * Push activity to stack. + *

+ * Must be called from {@link Activity#onCreate(Bundle)}. + * + * @param activity + */ + public void onCreate(Activity activity) { + if (LOG) + LogManager.i(activity, "onCreate: " + activity.getIntent()); + if (application.isClosing() && !(activity instanceof LoadActivity)) { + activity.startActivity(LoadActivity.createIntent(activity)); + activity.finish(); + } + activities.add(activity); + rebuildStack(); + fetchTaskIndex(activity, activity.getIntent()); + } - /** - * Pop activity from stack. - * - * Must be called from {@link Activity#onDestroy()}. - * - * @param activity - */ - public void onDestroy(Activity activity) { - if (LOG) - LogManager.i(activity, "onDestroy"); - activities.remove(activity); - } + /** + * Pop activity from stack. + *

+ * Must be called from {@link Activity#onDestroy()}. + * + * @param activity + */ + public void onDestroy(Activity activity) { + if (LOG) + LogManager.i(activity, "onDestroy"); + activities.remove(activity); + } - /** - * Pause activity. - * - * Must be called from {@link Activity#onPause()}. - * - * @param activity - */ - public void onPause(Activity activity) { - if (LOG) - LogManager.i(activity, "onPause"); - if (onErrorListener != null) - application - .removeUIListener(OnErrorListener.class, onErrorListener); - onErrorListener = null; - } + /** + * Pause activity. + *

+ * Must be called from {@link Activity#onPause()}. + * + * @param activity + */ + public void onPause(Activity activity) { + if (LOG) + LogManager.i(activity, "onPause"); + if (onErrorListener != null) + application + .removeUIListener(OnErrorListener.class, onErrorListener); + onErrorListener = null; + } - /** - * Resume activity. - * - * Must be called from {@link Activity#onResume()}. - * - * @param activity - */ - public void onResume(final Activity activity) { - if (LOG) - LogManager.i(activity, "onResume"); - if (!application.isInitialized() && !(activity instanceof LoadActivity)) { - if (LOG) - LogManager.i(this, "Wait for loading"); - activity.startActivity(LoadActivity.createIntent(activity)); - } - if (onErrorListener != null) - application - .removeUIListener(OnErrorListener.class, onErrorListener); - onErrorListener = new OnErrorListener() { - @Override - public void onError(final int resourceId) { - Toast.makeText(activity, activity.getString(resourceId), - Toast.LENGTH_LONG).show(); - } - }; - application.addUIListener(OnErrorListener.class, onErrorListener); - } + /** + * Resume activity. + *

+ * Must be called from {@link Activity#onResume()}. + * + * @param activity + */ + public void onResume(final Activity activity) { + if (LOG) + LogManager.i(activity, "onResume"); + if (!application.isInitialized() && !(activity instanceof LoadActivity)) { + if (LOG) + LogManager.i(this, "Wait for loading"); + activity.startActivity(LoadActivity.createIntent(activity)); + } + if (onErrorListener != null) { + application.removeUIListener(OnErrorListener.class, onErrorListener); + } + onErrorListener = new OnErrorListener() { + @Override + public void onError(final int resourceId) { + Toast.makeText(activity, activity.getString(resourceId), + Toast.LENGTH_LONG).show(); + } + }; + application.addUIListener(OnErrorListener.class, onErrorListener); + } - /** - * New intent received. - * - * Must be called from {@link Activity#onNewIntent(Intent)}. - * - * @param activity - * @param intent - */ - public void onNewIntent(Activity activity, Intent intent) { - if (LOG) - LogManager.i(activity, "onNewIntent: " + intent); - } + /** + * New intent received. + *

+ * Must be called from {@link Activity#onNewIntent(Intent)}. + * + * @param activity + * @param intent + */ + public void onNewIntent(Activity activity, Intent intent) { + if (LOG) + LogManager.i(activity, "onNewIntent: " + intent); + } - /** - * Result has been received. - * - * Must be called from {@link Activity#onActivityResult(int, int, Intent)}. - * - * @param activity - * @param requestCode - * @param resultCode - * @param data - */ - public void onActivityResult(Activity activity, int requestCode, - int resultCode, Intent data) { - if (LOG) - LogManager.i(activity, "onActivityResult: " + requestCode + ", " - + resultCode + ", " + data); - } + /** + * Result has been received. + *

+ * Must be called from {@link Activity#onActivityResult(int, int, Intent)}. + * + * @param activity + * @param requestCode + * @param resultCode + * @param data + */ + public void onActivityResult(Activity activity, int requestCode, + int resultCode, Intent data) { + if (LOG) + LogManager.i(activity, "onActivityResult: " + requestCode + ", " + + resultCode + ", " + data); + } - /** - * Adds task index to the intent if specified for the source activity. - * - * Must be used when source activity starts new own activity from - * {@link Activity#startActivity(Intent)} and - * {@link Activity#startActivityForResult(Intent, int)}. - * - * @param source - * @param intent - */ - public void updateIntent(Activity source, Intent intent) { - Integer index = taskIndexes.get(source); - if (index == null) - return; - intent.putExtra(EXTRA_TASK_INDEX, index); - } + /** + * Adds task index to the intent if specified for the source activity. + *

+ * Must be used when source activity starts new own activity from + * {@link Activity#startActivity(Intent)} and + * {@link Activity#startActivityForResult(Intent, int)}. + * + * @param source + * @param intent + */ + public void updateIntent(Activity source, Intent intent) { + Integer index = taskIndexes.get(source); + if (index == null) + return; + intent.putExtra(EXTRA_TASK_INDEX, index); + } - /** - * Mark activity to be in separate activity stack. - * - * @param activity - */ - public void startNewTask(Activity activity) { - taskIndexes.put(activity, nextTaskIndex); - LogManager.i(activity, "Start new task " + nextTaskIndex); - nextTaskIndex += 1; - } + /** + * Mark activity to be in separate activity stack. + * + * @param activity + */ + public void startNewTask(Activity activity) { + taskIndexes.put(activity, nextTaskIndex); + LogManager.i(activity, "Start new task " + nextTaskIndex); + nextTaskIndex += 1; + } - /** - * Either move main task to back, either close all activities in subtask. - * - * @param activity - */ - public void cancelTask(Activity activity) { - Integer index = taskIndexes.get(activity); - LogManager.i(activity, "Cancel task " + index); - if (index == null) { - activity.moveTaskToBack(true); - } else { - for (Entry entry : taskIndexes.entrySet()) - if (entry.getValue() == index) - entry.getKey().finish(); - } - } + /** + * Either move main task to back, either close all activities in subtask. + * + * @param activity + */ + public void cancelTask(Activity activity) { + Integer index = taskIndexes.get(activity); + LogManager.i(activity, "Cancel task " + index); + if (index == null) { + activity.moveTaskToBack(true); + } else { + for (Entry entry : taskIndexes.entrySet()) + if (entry.getValue() == index) + entry.getKey().finish(); + } + } - /** - * Fetch task index from the intent and mark specified activity. - * - * @param activity - * @param intent - */ - private void fetchTaskIndex(Activity activity, Intent intent) { - int index = intent.getIntExtra(EXTRA_TASK_INDEX, -1); - if (index == -1) - return; - LogManager.i(activity, "Fetch task index " + index); - taskIndexes.put(activity, index); - } + /** + * Fetch task index from the intent and mark specified activity. + * + * @param activity + * @param intent + */ + private void fetchTaskIndex(Activity activity, Intent intent) { + int index = intent.getIntExtra(EXTRA_TASK_INDEX, -1); + if (index == -1) + return; + LogManager.i(activity, "Fetch task index " + index); + taskIndexes.put(activity, index); + } - @Override - public void onUnload() { - clearStack(true); - } + @Override + public void onUnload() { + clearStack(true); + } } diff --git a/app/src/main/java/com/xabber/android/data/Application.java b/app/src/main/java/com/xabber/android/data/Application.java index d919ac7f99..d261d85fe5 100644 --- a/app/src/main/java/com/xabber/android/data/Application.java +++ b/app/src/main/java/com/xabber/android/data/Application.java @@ -1,19 +1,27 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data; +import android.app.Activity; +import android.content.pm.PackageManager; +import android.content.res.TypedArray; +import android.os.Handler; + +import com.xabber.android.R; +import com.xabber.android.service.XabberService; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -26,481 +34,455 @@ import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; -import android.app.Activity; -import android.content.pm.PackageManager; -import android.content.res.TypedArray; -import android.os.Build; -import android.os.Handler; - -import com.xabber.android.service.XabberService; -import com.xabber.androiddev.R; - /** * Base entry point. - * + * * @author alexander.ivanov */ public class Application extends android.app.Application { - public static final int SDK_INT = Integer.valueOf(Build.VERSION.SDK); - - private static Application instance; - - public static Application getInstance() { - if (instance == null) - throw new IllegalStateException(); - return instance; - } - - private final ArrayList registeredManagers; - - /** - * Unmodifiable collections of managers that implement some common - * interface. - */ - private Map, Collection> managerInterfaces; - - private Map, Collection> uiListeners; - - /** - * Thread to execute tasks in background.. - */ - private final ExecutorService backgroundExecutor; - - /** - * Handler to execute runnable in UI thread. - */ - private final Handler handler; - - /** - * Where data load was requested. - */ - private boolean serviceStarted; - - /** - * Whether application was initialized. - */ - private boolean initialized; - - /** - * Whether user was notified about some action in contact list activity - * after application initialization. - */ - private boolean notified; - - /** - * Whether application is to be closed. - */ - private boolean closing; - - /** - * Whether {@link #onServiceDestroy()} has been called. - */ - private boolean closed; - - /** - * Future for loading process. - */ - private Future loadFuture; - - private final Runnable timerRunnable = new Runnable() { - - @Override - public void run() { - for (OnTimerListener listener : getManagers(OnTimerListener.class)) - listener.onTimer(); - if (!closing) - startTimer(); - } - - }; - - public Application() { - instance = this; - serviceStarted = false; - initialized = false; - notified = false; - closing = false; - closed = false; - uiListeners = new HashMap, Collection>(); - managerInterfaces = new HashMap, Collection>(); - registeredManagers = new ArrayList(); - - handler = new Handler(); - backgroundExecutor = Executors - .newSingleThreadExecutor(new ThreadFactory() { - @Override - public Thread newThread(Runnable runnable) { - Thread thread = new Thread(runnable, - "Background executor service"); - thread.setPriority(Thread.MIN_PRIORITY); - thread.setDaemon(true); - return thread; - } - }); - } - - /** - * Whether application is initialized. - */ - public boolean isInitialized() { - return initialized; - } - - private void onLoad() { - for (OnLoadListener listener : getManagers(OnLoadListener.class)) { - LogManager.i(listener, "onLoad"); - listener.onLoad(); - } - } - - private void onInitialized() { - for (OnInitializedListener listener : getManagers(OnInitializedListener.class)) { - LogManager.i(listener, "onInitialized"); - listener.onInitialized(); - } - initialized = true; - XabberService.getInstance().changeForeground(); - startTimer(); - } - - private void onClose() { - LogManager.i(this, "onClose"); - for (Object manager : registeredManagers) - if (manager instanceof OnCloseListener) - ((OnCloseListener) manager).onClose(); - closed = true; - } - - private void onUnload() { - LogManager.i(this, "onUnload"); - for (Object manager : registeredManagers) - if (manager instanceof OnUnloadListener) - ((OnUnloadListener) manager).onUnload(); - android.os.Process.killProcess(android.os.Process.myPid()); - } - - /** - * @return true only once per application life. Subsequent - * calls will always returns false. - */ - public boolean doNotify() { - if (notified) - return false; - notified = true; - return true; - } - - /** - * Starts data loading in background if not started yet. - * - * @return - */ - public void onServiceStarted() { - if (serviceStarted) - return; - serviceStarted = true; - LogManager.i(this, "onStart"); - loadFuture = backgroundExecutor.submit(new Callable() { - @Override - public Void call() throws Exception { - try { - onLoad(); - } finally { - runOnUiThread(new Runnable() { - @Override - public void run() { - // Throw exceptions in UI thread if any. - try { - loadFuture.get(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } catch (ExecutionException e) { - throw new RuntimeException(e); - } - onInitialized(); - } - }); - } - return null; - } - }); - } - - /** - * Requests to close application in some time in future. - */ - public void requestToClose() { - closing = true; - stopService(XabberService.createIntent(this)); - } - - /** - * @return Whether application is to be closed. - */ - public boolean isClosing() { - return closing; - } - - /** - * Returns whether system contact storage is supported. - * - * Note: - * - * Please remove *_CONTACTS, *_ACCOUNTS, *_SETTINGS permissions, - * SyncAdapterService and AccountAuthenticatorService together from manifest - * file. - * - * @return - */ - public boolean isContactsSupported() { - return SDK_INT >= 5 - && checkCallingOrSelfPermission("android.permission.READ_CONTACTS") == PackageManager.PERMISSION_GRANTED; - } - - @Override - public void onCreate() { - super.onCreate(); - Thread.currentThread().setPriority(Thread.MAX_PRIORITY); - - ArrayList contactManager = new ArrayList(); - TypedArray contactManagerClasses = getResources().obtainTypedArray( - R.array.contact_managers); - for (int index = 0; index < contactManagerClasses.length(); index++) - contactManager.add(contactManagerClasses.getString(index)); - contactManagerClasses.recycle(); - - TypedArray managerClasses = getResources().obtainTypedArray( - R.array.managers); - for (int index = 0; index < managerClasses.length(); index++) - if (isContactsSupported() - || !contactManager - .contains(managerClasses.getString(index))) - try { - Class.forName(managerClasses.getString(index)); - } catch (ClassNotFoundException e) { - throw new RuntimeException(e); - } - managerClasses.recycle(); - - TypedArray tableClasses = getResources().obtainTypedArray( - R.array.tables); - for (int index = 0; index < tableClasses.length(); index++) - try { - Class.forName(tableClasses.getString(index)); - } catch (ClassNotFoundException e) { - throw new RuntimeException(e); - } - tableClasses.recycle(); - } - - @Override - public void onLowMemory() { - for (OnLowMemoryListener listener : getManagers(OnLowMemoryListener.class)) - listener.onLowMemory(); - super.onLowMemory(); - } - - /** - * Service have been destroyed. - */ - public void onServiceDestroy() { - if (closed) - return; - onClose(); - runInBackground(new Runnable() { - @Override - public void run() { - onUnload(); - } - }); - } - - @Override - public void onTerminate() { - requestToClose(); - super.onTerminate(); - } - - /** - * Start periodically callbacks. - */ - private void startTimer() { - runOnUiThreadDelay(timerRunnable, OnTimerListener.DELAY); - } - - /** - * Register new manager. - * - * @param manager - */ - public void addManager(Object manager) { - registeredManagers.add(manager); - } - - /** - * @param cls - * Requested class of managers. - * @return List of registered manager. - */ - @SuppressWarnings("unchecked") - public Collection getManagers( - Class cls) { - if (closed) - return Collections.emptyList(); - Collection collection = (Collection) managerInterfaces.get(cls); - if (collection == null) { - collection = new ArrayList(); - for (Object manager : registeredManagers) - if (cls.isInstance(manager)) - collection.add((T) manager); - collection = Collections.unmodifiableCollection(collection); - managerInterfaces.put(cls, collection); - } - return collection; - } - - /** - * Request to clear application data. - */ - public void requestToClear() { - runInBackground(new Runnable() { - @Override - public void run() { - clear(); - } - }); - } - - private void clear() { - for (Object manager : registeredManagers) - if (manager instanceof OnClearListener) - ((OnClearListener) manager).onClear(); - } - - /** - * Request to wipe all sensitive application data. - */ - public void requestToWipe() { - runInBackground(new Runnable() { - @Override - public void run() { - clear(); - for (Object manager : registeredManagers) - if (manager instanceof OnWipeListener) - ((OnWipeListener) manager).onWipe(); - } - }); - } - - @SuppressWarnings("unchecked") - private Collection getOrCreateUIListeners( - Class cls) { - Collection collection = (Collection) uiListeners.get(cls); - if (collection == null) { - collection = new ArrayList(); - uiListeners.put(cls, collection); - } - return collection; - } - - /** - * @param cls - * Requested class of listeners. - * @return List of registered UI listeners. - */ - public Collection getUIListeners(Class cls) { - if (closed) - return Collections.emptyList(); - return Collections.unmodifiableCollection(getOrCreateUIListeners(cls)); - } - - /** - * Register new listener. - * - * Should be called from {@link Activity#onResume()}. - * - * @param cls - * @param listener - */ - public void addUIListener(Class cls, - T listener) { - getOrCreateUIListeners(cls).add(listener); - } - - /** - * Unregister listener. - * - * Should be called from {@link Activity#onPause()}. - * - * @param cls - * @param listener - */ - public void removeUIListener(Class cls, - T listener) { - getOrCreateUIListeners(cls).remove(listener); - } - - /** - * Notify about error. - * - * @param resourceId - */ - public void onError(final int resourceId) { - runOnUiThread(new Runnable() { - @Override - public void run() { - for (OnErrorListener onErrorListener : getUIListeners(OnErrorListener.class)) - onErrorListener.onError(resourceId); - } - }); - } - - /** - * Notify about error. - * - * @param networkException - */ - public void onError(NetworkException networkException) { - LogManager.exception(this, networkException); - onError(networkException.getResourceId()); - } - - /** - * Submits request to be executed in background. - * - * @param runnable - */ - public void runInBackground(final Runnable runnable) { - backgroundExecutor.submit(new Runnable() { - @Override - public void run() { - try { - runnable.run(); - } catch (Exception e) { - LogManager.exception(runnable, e); - } - } - }); - } - - /** - * Submits request to be executed in UI thread. - * - * @param runnable - */ - public void runOnUiThread(final Runnable runnable) { - handler.post(runnable); - } - - /** - * Submits request to be executed in UI thread. - * - * @param runnable - * @param delayMillis - */ - public void runOnUiThreadDelay(final Runnable runnable, long delayMillis) { - handler.postDelayed(runnable, delayMillis); - } + private static Application instance; + private final ArrayList registeredManagers; + /** + * Thread to execute tasks in background.. + */ + private final ExecutorService backgroundExecutor; + /** + * Handler to execute runnable in UI thread. + */ + private final Handler handler; + /** + * Unmodifiable collections of managers that implement some common + * interface. + */ + private Map, Collection> managerInterfaces; + private Map, Collection> uiListeners; + /** + * Where data load was requested. + */ + private boolean serviceStarted; + /** + * Whether application was initialized. + */ + private boolean initialized; + /** + * Whether user was notified about some action in contact list activity + * after application initialization. + */ + private boolean notified; + /** + * Whether application is to be closed. + */ + private boolean closing; + /** + * Whether {@link #onServiceDestroy()} has been called. + */ + private boolean closed; + private final Runnable timerRunnable = new Runnable() { + + @Override + public void run() { + for (OnTimerListener listener : getManagers(OnTimerListener.class)) + listener.onTimer(); + if (!closing) + startTimer(); + } + + }; + /** + * Future for loading process. + */ + private Future loadFuture; + + public Application() { + instance = this; + serviceStarted = false; + initialized = false; + notified = false; + closing = false; + closed = false; + uiListeners = new HashMap, Collection>(); + managerInterfaces = new HashMap, Collection>(); + registeredManagers = new ArrayList(); + + handler = new Handler(); + backgroundExecutor = Executors + .newSingleThreadExecutor(new ThreadFactory() { + @Override + public Thread newThread(Runnable runnable) { + Thread thread = new Thread(runnable, + "Background executor service"); + thread.setPriority(Thread.MIN_PRIORITY); + thread.setDaemon(true); + return thread; + } + }); + } + + public static Application getInstance() { + if (instance == null) + throw new IllegalStateException(); + return instance; + } + + /** + * Whether application is initialized. + */ + public boolean isInitialized() { + return initialized; + } + + private void onLoad() { + for (OnLoadListener listener : getManagers(OnLoadListener.class)) { + LogManager.i(listener, "onLoad"); + listener.onLoad(); + } + } + + private void onInitialized() { + for (OnInitializedListener listener : getManagers(OnInitializedListener.class)) { + LogManager.i(listener, "onInitialized"); + listener.onInitialized(); + } + initialized = true; + XabberService.getInstance().changeForeground(); + startTimer(); + } + + private void onClose() { + LogManager.i(this, "onClose"); + for (Object manager : registeredManagers) + if (manager instanceof OnCloseListener) + ((OnCloseListener) manager).onClose(); + closed = true; + } + + private void onUnload() { + LogManager.i(this, "onUnload"); + for (Object manager : registeredManagers) + if (manager instanceof OnUnloadListener) + ((OnUnloadListener) manager).onUnload(); + android.os.Process.killProcess(android.os.Process.myPid()); + } + + /** + * @return true only once per application life. Subsequent + * calls will always returns false. + */ + public boolean doNotify() { + if (notified) + return false; + notified = true; + return true; + } + + /** + * Starts data loading in background if not started yet. + * + * @return + */ + public void onServiceStarted() { + if (serviceStarted) + return; + serviceStarted = true; + LogManager.i(this, "onStart"); + loadFuture = backgroundExecutor.submit(new Callable() { + @Override + public Void call() throws Exception { + try { + onLoad(); + } finally { + runOnUiThread(new Runnable() { + @Override + public void run() { + // Throw exceptions in UI thread if any. + try { + loadFuture.get(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } + onInitialized(); + } + }); + } + return null; + } + }); + } + + /** + * Requests to close application in some time in future. + */ + public void requestToClose() { + closing = true; + stopService(XabberService.createIntent(this)); + } + + /** + * @return Whether application is to be closed. + */ + public boolean isClosing() { + return closing; + } + + /** + * Returns whether system contact storage is supported. + *

+ * Note: + *

+ * Please remove *_CONTACTS, *_ACCOUNTS, *_SETTINGS permissions, + * SyncAdapterService and AccountAuthenticatorService together from manifest + * file. + * + * @return + */ + public boolean isContactsSupported() { + return checkCallingOrSelfPermission("android.permission.READ_CONTACTS") == PackageManager.PERMISSION_GRANTED; + } + + @Override + public void onCreate() { + super.onCreate(); + Thread.currentThread().setPriority(Thread.MAX_PRIORITY); + + ArrayList contactManager = new ArrayList(); + TypedArray contactManagerClasses = getResources().obtainTypedArray( + R.array.contact_managers); + for (int index = 0; index < contactManagerClasses.length(); index++) + contactManager.add(contactManagerClasses.getString(index)); + contactManagerClasses.recycle(); + + TypedArray managerClasses = getResources().obtainTypedArray( + R.array.managers); + for (int index = 0; index < managerClasses.length(); index++) + if (isContactsSupported() + || !contactManager + .contains(managerClasses.getString(index))) + try { + Class.forName(managerClasses.getString(index)); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + managerClasses.recycle(); + + TypedArray tableClasses = getResources().obtainTypedArray( + R.array.tables); + for (int index = 0; index < tableClasses.length(); index++) + try { + Class.forName(tableClasses.getString(index)); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + tableClasses.recycle(); + } + + @Override + public void onLowMemory() { + for (OnLowMemoryListener listener : getManagers(OnLowMemoryListener.class)) + listener.onLowMemory(); + super.onLowMemory(); + } + + /** + * Service have been destroyed. + */ + public void onServiceDestroy() { + if (closed) + return; + onClose(); + runInBackground(new Runnable() { + @Override + public void run() { + onUnload(); + } + }); + } + + @Override + public void onTerminate() { + requestToClose(); + super.onTerminate(); + } + + /** + * Start periodically callbacks. + */ + private void startTimer() { + runOnUiThreadDelay(timerRunnable, OnTimerListener.DELAY); + } + + /** + * Register new manager. + * + * @param manager + */ + public void addManager(Object manager) { + registeredManagers.add(manager); + } + + /** + * @param cls Requested class of managers. + * @return List of registered manager. + */ + @SuppressWarnings("unchecked") + public Collection getManagers( + Class cls) { + if (closed) + return Collections.emptyList(); + Collection collection = (Collection) managerInterfaces.get(cls); + if (collection == null) { + collection = new ArrayList(); + for (Object manager : registeredManagers) + if (cls.isInstance(manager)) + collection.add((T) manager); + collection = Collections.unmodifiableCollection(collection); + managerInterfaces.put(cls, collection); + } + return collection; + } + + /** + * Request to clear application data. + */ + public void requestToClear() { + runInBackground(new Runnable() { + @Override + public void run() { + clear(); + } + }); + } + + private void clear() { + for (Object manager : registeredManagers) + if (manager instanceof OnClearListener) + ((OnClearListener) manager).onClear(); + } + + /** + * Request to wipe all sensitive application data. + */ + public void requestToWipe() { + runInBackground(new Runnable() { + @Override + public void run() { + clear(); + for (Object manager : registeredManagers) + if (manager instanceof OnWipeListener) + ((OnWipeListener) manager).onWipe(); + } + }); + } + + @SuppressWarnings("unchecked") + private Collection getOrCreateUIListeners( + Class cls) { + Collection collection = (Collection) uiListeners.get(cls); + if (collection == null) { + collection = new ArrayList(); + uiListeners.put(cls, collection); + } + return collection; + } + + /** + * @param cls Requested class of listeners. + * @return List of registered UI listeners. + */ + public Collection getUIListeners(Class cls) { + if (closed) + return Collections.emptyList(); + return Collections.unmodifiableCollection(getOrCreateUIListeners(cls)); + } + + /** + * Register new listener. + *

+ * Should be called from {@link Activity#onResume()}. + * + * @param cls + * @param listener + */ + public void addUIListener(Class cls, + T listener) { + getOrCreateUIListeners(cls).add(listener); + } + + /** + * Unregister listener. + *

+ * Should be called from {@link Activity#onPause()}. + * + * @param cls + * @param listener + */ + public void removeUIListener(Class cls, + T listener) { + getOrCreateUIListeners(cls).remove(listener); + } + + /** + * Notify about error. + * + * @param resourceId + */ + public void onError(final int resourceId) { + runOnUiThread(new Runnable() { + @Override + public void run() { + for (OnErrorListener onErrorListener : getUIListeners(OnErrorListener.class)) + onErrorListener.onError(resourceId); + } + }); + } + + /** + * Notify about error. + * + * @param networkException + */ + public void onError(NetworkException networkException) { + LogManager.exception(this, networkException); + onError(networkException.getResourceId()); + } + + /** + * Submits request to be executed in background. + * + * @param runnable + */ + public void runInBackground(final Runnable runnable) { + backgroundExecutor.submit(new Runnable() { + @Override + public void run() { + try { + runnable.run(); + } catch (Exception e) { + LogManager.exception(runnable, e); + } + } + }); + } + + /** + * Submits request to be executed in UI thread. + * + * @param runnable + */ + public void runOnUiThread(final Runnable runnable) { + handler.post(runnable); + } + + /** + * Submits request to be executed in UI thread. + * + * @param runnable + * @param delayMillis + */ + public void runOnUiThreadDelay(final Runnable runnable, long delayMillis) { + handler.postDelayed(runnable, delayMillis); + } } diff --git a/app/src/main/java/com/xabber/android/data/BaseManagerInterface.java b/app/src/main/java/com/xabber/android/data/BaseManagerInterface.java index 0ad3490e37..e7a85f9c23 100644 --- a/app/src/main/java/com/xabber/android/data/BaseManagerInterface.java +++ b/app/src/main/java/com/xabber/android/data/BaseManagerInterface.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,9 +16,8 @@ /** * Base interface common for the registered manager. - * + * * @author alexander.ivanov - * */ public interface BaseManagerInterface { diff --git a/app/src/main/java/com/xabber/android/data/BaseUIListener.java b/app/src/main/java/com/xabber/android/data/BaseUIListener.java index a60ec56e0a..c09c1227e4 100644 --- a/app/src/main/java/com/xabber/android/data/BaseUIListener.java +++ b/app/src/main/java/com/xabber/android/data/BaseUIListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,12 +18,11 @@ /** * Base listener to notify UI about some changes. - * + *

* This listener should be registered from {@link Activity#onResume()} and * unregistered from {@link Activity#onPause()}. - * + * * @author alexander.ivanov - * */ public interface BaseUIListener { diff --git a/app/src/main/java/com/xabber/android/data/DatabaseManager.java b/app/src/main/java/com/xabber/android/data/DatabaseManager.java index 5a5dcc4f49..27b1b90bcb 100644 --- a/app/src/main/java/com/xabber/android/data/DatabaseManager.java +++ b/app/src/main/java/com/xabber/android/data/DatabaseManager.java @@ -1,25 +1,19 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data; -import java.io.File; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; - import android.database.DatabaseUtils; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; @@ -27,200 +21,205 @@ import com.xabber.android.data.entity.AbstractAccountTable; +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; + /** * Helps to open, create, and upgrade the database file. - * + *

* All requests to database / file system MUST be called from background thread. - * + * * @author alexander.ivanov */ public class DatabaseManager extends SQLiteOpenHelper implements - OnLoadListener, OnClearListener { - - private static final String DATABASE_NAME = "xabber.db"; - private static final int DATABASE_VERSION = 66; - - private static final SQLiteException DOWNGRAD_EXCEPTION = new SQLiteException( - "Database file was deleted"); - - private final ArrayList registeredTables; - - private final static DatabaseManager instance; - - static { - instance = new DatabaseManager(); - Application.getInstance().addManager(instance); - } - - public static DatabaseManager getInstance() { - return instance; - } - - private DatabaseManager() { - super(Application.getInstance(), DATABASE_NAME, null, DATABASE_VERSION); - registeredTables = new ArrayList(); - } - - /** - * Register new table. - * - * @param table - */ - public void addTable(DatabaseTable table) { - registeredTables.add(table); - } - - @Override - public void onLoad() { - try { - getWritableDatabase(); // Force onCreate or onUpgrade - } catch (SQLiteException e) { - if (e == DOWNGRAD_EXCEPTION) { - // Downgrade occured - } else { - throw e; - } - } - } - - @Override - public void onCreate(SQLiteDatabase db) { - for (DatabaseTable table : registeredTables) - table.create(db); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - if (oldVersion > newVersion) { - LogManager.i(this, "Downgrading database from version " - + oldVersion + " to " + newVersion); - File file = new File(db.getPath()); - file.delete(); - LogManager.i(this, "Database file was deleted"); - throw DOWNGRAD_EXCEPTION; - // This will interrupt getWritableDatabase() call from - // DatabaseManager's constructor. - } else { - LogManager.i(this, "Upgrading database from version " + oldVersion - + " to " + newVersion); - while (oldVersion < newVersion) { - oldVersion += 1; - LogManager.i(this, "Migrate to version " + oldVersion); - migrate(db, oldVersion); - for (DatabaseTable table : registeredTables) - table.migrate(db, oldVersion); - for (OnMigrationListener listener : Application.getInstance() - .getManagers(OnMigrationListener.class)) - listener.onMigrate(oldVersion); - } - } - } - - /** - * Called on database migration. - * - * @param db - * @param toVersion - */ - private void migrate(SQLiteDatabase db, int toVersion) { - switch (toVersion) { - case 42: - dropTable(db, "geolocs"); - dropTable(db, "locations"); - break; - default: - break; - } - } - - @Override - public void onClear() { - for (DatabaseTable table : registeredTables) - table.clear(); - } - - public void removeAccount(String account) { - // TODO: replace with constraint. - for (DatabaseTable table : registeredTables) - if (table instanceof AbstractAccountTable) - ((AbstractAccountTable) table).removeAccount(account); - } - - /** - * Builds IN statement for specified collection of values. - * - * @param - * @param column - * @param values - * @return "column IN (value1, ... valueN)" or - * "(column IS NULL AND column IS NOT NULL)" if ids is empty. - */ - public static String in(String column, Collection values) { - if (values.isEmpty()) - return new StringBuilder("(").append(column) - .append(" IS NULL AND ").append(column) - .append(" IS NOT NULL)").toString(); - StringBuilder builder = new StringBuilder(column); - builder.append(" IN ("); - Iterator iterator = values.iterator(); - while (iterator.hasNext()) { - T value = iterator.next(); - if (value instanceof String) - builder.append(DatabaseUtils.sqlEscapeString((String) value)); - else - builder.append(value.toString()); - if (iterator.hasNext()) - builder.append(","); - } - builder.append(")"); - return builder.toString(); - } - - public static void execSQL(SQLiteDatabase db, String sql) { - LogManager.iString(DatabaseManager.class.getName(), sql); - db.execSQL(sql); - } - - public static void dropTable(SQLiteDatabase db, String table) { - execSQL(db, "DROP TABLE IF EXISTS " + table + ";"); - } - - public static void renameTable(SQLiteDatabase db, String table, - String newTable) { - execSQL(db, "ALTER TABLE " + table + " RENAME TO " + newTable + ";"); - } - - public static String commaSeparatedFromCollection(Collection strings) { - StringBuilder builder = new StringBuilder(); - for (String value : strings) { - if (builder.length() > 0) - builder.append(","); - builder.append(value.replace("\\", "\\\\").replace(",", "\\,")); - } - return builder.toString(); - } - - public static Collection collectionFromCommaSeparated(String value) { - Collection collection = new ArrayList(); - boolean escape = false; - StringBuilder builder = new StringBuilder(); - for (int index = 0; index < value.length(); index++) { - char chr = value.charAt(index); - if (!escape) { - if (chr == '\\') { - escape = true; - continue; - } else if (chr == ',') { - collection.add(builder.toString()); - builder = new StringBuilder(); - continue; - } - } - escape = false; - builder.append(chr); - } - collection.add(builder.toString()); - return Collections.unmodifiableCollection(collection); - } + OnLoadListener, OnClearListener { + + private static final String DATABASE_NAME = "xabber.db"; + private static final int DATABASE_VERSION = 68; + + private static final SQLiteException DOWNGRAD_EXCEPTION = new SQLiteException( + "Database file was deleted"); + private final static DatabaseManager instance; + + static { + instance = new DatabaseManager(); + Application.getInstance().addManager(instance); + } + + private final ArrayList registeredTables; + + private DatabaseManager() { + super(Application.getInstance(), DATABASE_NAME, null, DATABASE_VERSION); + registeredTables = new ArrayList(); + } + + public static DatabaseManager getInstance() { + return instance; + } + + /** + * Builds IN statement for specified collection of values. + * + * @param + * @param column + * @param values + * @return "column IN (value1, ... valueN)" or + * "(column IS NULL AND column IS NOT NULL)" if ids is empty. + */ + public static String in(String column, Collection values) { + if (values.isEmpty()) + return new StringBuilder("(").append(column) + .append(" IS NULL AND ").append(column) + .append(" IS NOT NULL)").toString(); + StringBuilder builder = new StringBuilder(column); + builder.append(" IN ("); + Iterator iterator = values.iterator(); + while (iterator.hasNext()) { + T value = iterator.next(); + if (value instanceof String) + builder.append(DatabaseUtils.sqlEscapeString((String) value)); + else + builder.append(value.toString()); + if (iterator.hasNext()) + builder.append(","); + } + builder.append(")"); + return builder.toString(); + } + + public static void execSQL(SQLiteDatabase db, String sql) { + LogManager.iString(DatabaseManager.class.getName(), sql); + db.execSQL(sql); + } + + public static void dropTable(SQLiteDatabase db, String table) { + execSQL(db, "DROP TABLE IF EXISTS " + table + ";"); + } + + public static void renameTable(SQLiteDatabase db, String table, + String newTable) { + execSQL(db, "ALTER TABLE " + table + " RENAME TO " + newTable + ";"); + } + + public static String commaSeparatedFromCollection(Collection strings) { + StringBuilder builder = new StringBuilder(); + for (String value : strings) { + if (builder.length() > 0) + builder.append(","); + builder.append(value.replace("\\", "\\\\").replace(",", "\\,")); + } + return builder.toString(); + } + + public static Collection collectionFromCommaSeparated(String value) { + Collection collection = new ArrayList(); + boolean escape = false; + StringBuilder builder = new StringBuilder(); + for (int index = 0; index < value.length(); index++) { + char chr = value.charAt(index); + if (!escape) { + if (chr == '\\') { + escape = true; + continue; + } else if (chr == ',') { + collection.add(builder.toString()); + builder = new StringBuilder(); + continue; + } + } + escape = false; + builder.append(chr); + } + collection.add(builder.toString()); + return Collections.unmodifiableCollection(collection); + } + + /** + * Register new table. + * + * @param table + */ + public void addTable(DatabaseTable table) { + registeredTables.add(table); + } + + @Override + public void onLoad() { + try { + getWritableDatabase(); // Force onCreate or onUpgrade + } catch (SQLiteException e) { + if (e == DOWNGRAD_EXCEPTION) { + // Downgrade occured + } else { + throw e; + } + } + } + + @Override + public void onCreate(SQLiteDatabase db) { + for (DatabaseTable table : registeredTables) + table.create(db); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + if (oldVersion > newVersion) { + LogManager.i(this, "Downgrading database from version " + + oldVersion + " to " + newVersion); + File file = new File(db.getPath()); + file.delete(); + LogManager.i(this, "Database file was deleted"); + throw DOWNGRAD_EXCEPTION; + // This will interrupt getWritableDatabase() call from + // DatabaseManager's constructor. + } else { + LogManager.i(this, "Upgrading database from version " + oldVersion + + " to " + newVersion); + while (oldVersion < newVersion) { + oldVersion += 1; + LogManager.i(this, "Migrate to version " + oldVersion); + migrate(db, oldVersion); + for (DatabaseTable table : registeredTables) + table.migrate(db, oldVersion); + for (OnMigrationListener listener : Application.getInstance() + .getManagers(OnMigrationListener.class)) + listener.onMigrate(oldVersion); + } + } + } + + /** + * Called on database migration. + * + * @param db + * @param toVersion + */ + private void migrate(SQLiteDatabase db, int toVersion) { + switch (toVersion) { + case 42: + dropTable(db, "geolocs"); + dropTable(db, "locations"); + break; + default: + break; + } + } + + @Override + public void onClear() { + for (DatabaseTable table : registeredTables) + table.clear(); + } + + public void removeAccount(String account) { + // TODO: replace with constraint. + for (DatabaseTable table : registeredTables) + if (table instanceof AbstractAccountTable) + ((AbstractAccountTable) table).removeAccount(account); + } } diff --git a/app/src/main/java/com/xabber/android/data/DatabaseTable.java b/app/src/main/java/com/xabber/android/data/DatabaseTable.java index 49082cf621..bc8f07af59 100644 --- a/app/src/main/java/com/xabber/android/data/DatabaseTable.java +++ b/app/src/main/java/com/xabber/android/data/DatabaseTable.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,30 +18,29 @@ /** * Interface for registered database tables. - * + * * @author alexander.ivanov - * */ public interface DatabaseTable { - /** - * Called on create database. - * - * @param db - */ - void create(SQLiteDatabase db); + /** + * Called on create database. + * + * @param db + */ + void create(SQLiteDatabase db); - /** - * Called on database migration. - * - * @param db - * @param toVersion - */ - void migrate(SQLiteDatabase db, int toVersion); + /** + * Called on database migration. + * + * @param db + * @param toVersion + */ + void migrate(SQLiteDatabase db, int toVersion); - /** - * Called on clear database request. - */ - void clear(); + /** + * Called on clear database request. + */ + void clear(); } diff --git a/app/src/main/java/com/xabber/android/data/FileLogDebugger.java b/app/src/main/java/com/xabber/android/data/FileLogDebugger.java index 7dac81f1db..9e83dae867 100644 --- a/app/src/main/java/com/xabber/android/data/FileLogDebugger.java +++ b/app/src/main/java/com/xabber/android/data/FileLogDebugger.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -31,134 +31,133 @@ /** * Writer for connection log to the file. - * + * * @author alexander.ivanov - * */ public class FileLogDebugger extends ConsoleDebugger { - private FileWriter writer; - private boolean readerClosed; - private boolean writerClosed; + private FileWriter writer; + private boolean readerClosed; + private boolean writerClosed; - private final static SimpleDateFormat FILE_NAME_FORMAT = new SimpleDateFormat( - "yyyy-MM-dd-HH-mm-ss"); + private final static SimpleDateFormat FILE_NAME_FORMAT = new SimpleDateFormat( + "yyyy-MM-dd-HH-mm-ss"); - public FileLogDebugger(Connection connection, Writer writer, Reader reader) { - super(connection, writer, reader); - File dir = new File(Environment.getExternalStorageDirectory(), - "xabber-log"); - dir.mkdirs(); - File file = new File(dir, FILE_NAME_FORMAT.format(new Date()) + " - " - + connection.hashCode() + ".xml"); - try { - this.writer = new FileWriter(file); - this.writer.write(""); - this.writer.write("\n"); - this.writer.flush(); - } catch (IOException e) { - LogManager.forceException(this, e); - } - } + public FileLogDebugger(Connection connection, Writer writer, Reader reader) { + super(connection, writer, reader); + File dir = new File(Environment.getExternalStorageDirectory(), + "xabber-log"); + dir.mkdirs(); + File file = new File(dir, FILE_NAME_FORMAT.format(new Date()) + " - " + + connection.hashCode() + ".xml"); + try { + this.writer = new FileWriter(file); + this.writer.write(""); + this.writer.write("\n"); + this.writer.flush(); + } catch (IOException e) { + LogManager.forceException(this, e); + } + } - @Override - protected ReaderListener createReaderListener() { - final ReaderListener inherited = super.createReaderListener(); - return new ReaderListener() { - @Override - public void read(String str) { - inherited.read(str); - synchronized (this) { - if (writerClosed && readerClosed) - return; - try { - writer.write("\n"); - writer.write(dateFormatter.format(new Date())); - writer.write(" RCV "); - writer.write(str); - writer.flush(); - } catch (IOException e) { - LogManager.forceException(this, e); - } - } - } + @Override + protected ReaderListener createReaderListener() { + final ReaderListener inherited = super.createReaderListener(); + return new ReaderListener() { + @Override + public void read(String str) { + inherited.read(str); + synchronized (this) { + if (writerClosed && readerClosed) + return; + try { + writer.write("\n"); + writer.write(dateFormatter.format(new Date())); + writer.write(" RCV "); + writer.write(str); + writer.flush(); + } catch (IOException e) { + LogManager.forceException(this, e); + } + } + } - @Override - public void close() { - inherited.close(); - System.out.println(dateFormatter.format(new Date()) - + " RCV CLOSED (" + connection.hashCode() + ")"); - synchronized (this) { - if (readerClosed) - return; - try { - writer.write("\n"); - writer.write(dateFormatter.format(new Date())); - writer.write(" RCV - CLOSED "); - writer.flush(); - } catch (IOException e) { - LogManager.exception(this, e); - } - readerClosed = true; - onClose(); - } - } - }; - } + @Override + public void close() { + inherited.close(); + System.out.println(dateFormatter.format(new Date()) + + " RCV CLOSED (" + connection.hashCode() + ")"); + synchronized (this) { + if (readerClosed) + return; + try { + writer.write("\n"); + writer.write(dateFormatter.format(new Date())); + writer.write(" RCV - CLOSED "); + writer.flush(); + } catch (IOException e) { + LogManager.exception(this, e); + } + readerClosed = true; + onClose(); + } + } + }; + } - @Override - protected WriterListener createWriterListener() { - final WriterListener inherited = super.createWriterListener(); - return new WriterListener() { - @Override - public void write(String str) { - inherited.write(str); - synchronized (this) { - if (writerClosed && readerClosed) - return; - try { - writer.write("\n"); - writer.write(dateFormatter.format(new Date())); - writer.write(" SNT "); - writer.write(str); - writer.flush(); - } catch (IOException e) { - LogManager.forceException(this, e); - } - } - } + @Override + protected WriterListener createWriterListener() { + final WriterListener inherited = super.createWriterListener(); + return new WriterListener() { + @Override + public void write(String str) { + inherited.write(str); + synchronized (this) { + if (writerClosed && readerClosed) + return; + try { + writer.write("\n"); + writer.write(dateFormatter.format(new Date())); + writer.write(" SNT "); + writer.write(str); + writer.flush(); + } catch (IOException e) { + LogManager.forceException(this, e); + } + } + } - @Override - public void close() { - inherited.close(); - System.out.println(dateFormatter.format(new Date()) - + " SENT CLOSED (" + connection.hashCode() + ")"); - synchronized (this) { - if (writerClosed) - return; - try { - writer.write("\n"); - writer.write(dateFormatter.format(new Date())); - writer.write(" SNT - CLOSED"); - writer.flush(); - } catch (IOException e) { - LogManager.exception(this, e); - } - writerClosed = true; - onClose(); - } - } - }; - } + @Override + public void close() { + inherited.close(); + System.out.println(dateFormatter.format(new Date()) + + " SENT CLOSED (" + connection.hashCode() + ")"); + synchronized (this) { + if (writerClosed) + return; + try { + writer.write("\n"); + writer.write(dateFormatter.format(new Date())); + writer.write(" SNT - CLOSED"); + writer.flush(); + } catch (IOException e) { + LogManager.exception(this, e); + } + writerClosed = true; + onClose(); + } + } + }; + } - private void onClose() { - if (writerClosed && readerClosed) { - try { - writer.write("\n"); - writer.write(""); - writer.close(); - } catch (IOException e) { - LogManager.exception(this, e); - } - } - } + private void onClose() { + if (writerClosed && readerClosed) { + try { + writer.write("\n"); + writer.write(""); + writer.close(); + } catch (IOException e) { + LogManager.exception(this, e); + } + } + } } diff --git a/app/src/main/java/com/xabber/android/data/LogManager.java b/app/src/main/java/com/xabber/android/data/LogManager.java index 2668e7945b..e31907f611 100644 --- a/app/src/main/java/com/xabber/android/data/LogManager.java +++ b/app/src/main/java/com/xabber/android/data/LogManager.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,196 +16,149 @@ import java.io.PrintWriter; import java.io.StringWriter; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import org.jivesoftware.smack.Connection; import org.xbill.DNS.Options; -import android.content.Context; import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager.NameNotFoundException; import android.util.Log; /** * Manager to write to the log. - * + * * @author alexander.ivanov - * */ public class LogManager implements OnLoadListener { - private static final boolean log; - private static final boolean debugable; - private static Method _getApplicationInfo; - - static { - initCompatibility(); - debugable = (getApplicationInfo(Application.getInstance()).flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; - log = debugable && SettingsManager.debugLog(); - }; - - private static void initCompatibility() { - try { - _getApplicationInfo = Context.class.getMethod("getApplicationInfo", - new Class[] {}); - } catch (NoSuchMethodException nsme) { - } - } - - public static ApplicationInfo getApplicationInfo(Context context) { - ApplicationInfo applicationInfo; - if (_getApplicationInfo != null) { - try { - applicationInfo = (ApplicationInfo) _getApplicationInfo - .invoke(context); - } catch (InvocationTargetException e) { - Throwable cause = e.getCause(); - if (cause instanceof RuntimeException) { - throw (RuntimeException) cause; - } else if (cause instanceof Error) { - throw (Error) cause; - } else { - throw new RuntimeException(e); - } - } catch (IllegalAccessException ie) { - throw new RuntimeException(ie); - } - } else { - try { - applicationInfo = context.getPackageManager() - .getApplicationInfo(context.getPackageName(), 0); - } catch (NameNotFoundException e) { - Log.e("LogManager", - "I can`t find my package in the system. Debug will be disabled."); - applicationInfo = new ApplicationInfo(); - applicationInfo.flags = 0; - } - } - return applicationInfo; - } - - private final static LogManager instance; - - static { - instance = new LogManager(Application.getInstance()); - Application.getInstance().addManager(instance); - } - - public static LogManager getInstance() { - return instance; - } - - private LogManager(Application application) { - } - - @Override - public void onLoad() { - if (log) { - System.setProperty("smack.debuggerClass", - "org.jivesoftware.smack.debugger.ConsoleDebugger"); - // "com.xabber.android.data.FileLogDebugger"); - System.setProperty("smack.debugEnabled", "true"); - Connection.DEBUG_ENABLED = true; - Options.set("verbose"); - Options.set("verbosemsg"); - Options.set("verbosecompression"); - Options.set("verbosesec"); - Options.set("verbosecache"); - } - } - - static public int dString(String tag, String msg) { - if (log) - return Log.d(tag, msg); - else - return 0; - } - - static public int eString(String tag, String msg) { - if (log) - return Log.e(tag, msg); - else - return 0; - } - - static public int iString(String tag, String msg) { - if (log) - return Log.i(tag, msg); - else - return 0; - } - - static public int wString(String tag, String msg) { - if (log) - return Log.w(tag, msg); - else - return 0; - } - - static public int vString(String tag, String msg) { - if (log) - return Log.v(tag, msg); - else - return 0; - } - - static public int d(Object obj, String msg) { - return dString(obj.toString(), msg); - } - - static public int e(Object obj, String msg) { - return eString(obj.toString(), msg); - } - - static public int i(Object obj, String msg) { - return iString(obj.toString(), msg); - } - - static public int w(Object obj, String msg) { - return wString(obj.toString(), msg); - } - - static public int v(Object obj, String msg) { - return vString(obj.toString(), msg); - } - - /** - * Print stack trace if log is enabled. - * - * @param obj - * @param exception - */ - public static void exception(Object obj, Exception exception) { - if (!log) - return; - forceException(obj, exception); - } - - /** - * Print stack trace even if log is disabled. - * - * @param obj - * @param exception - */ - public static void forceException(Object obj, Exception exception) { - System.err.println(obj.toString()); - System.err.println(getStackTrace(exception)); - } - - /** - * @param exception - * @return stack trace. - */ - private static String getStackTrace(Exception exception) { - final StringWriter result = new StringWriter(); - final PrintWriter printWriter = new PrintWriter(result); - exception.printStackTrace(printWriter); - return result.toString(); - } - - public static boolean isDebugable() { - return debugable; - } + private static final boolean log; + private static final boolean debugable; + + static { + debugable = (Application.getInstance().getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; + log = debugable && SettingsManager.debugLog(); + } + + private final static LogManager instance; + + static { + instance = new LogManager(Application.getInstance()); + Application.getInstance().addManager(instance); + } + + public static LogManager getInstance() { + return instance; + } + + private LogManager(Application application) { + } + + @Override + public void onLoad() { + if (log) { + System.setProperty("smack.debuggerClass", + "org.jivesoftware.smack.debugger.ConsoleDebugger"); + // "com.xabber.android.data.FileLogDebugger"); + System.setProperty("smack.debugEnabled", "true"); + Connection.DEBUG_ENABLED = true; + Options.set("verbose"); + Options.set("verbosemsg"); + Options.set("verbosecompression"); + Options.set("verbosesec"); + Options.set("verbosecache"); + } + } + + static public int dString(String tag, String msg) { + if (log) + return Log.d(tag, msg); + else + return 0; + } + + static public int eString(String tag, String msg) { + if (log) + return Log.e(tag, msg); + else + return 0; + } + + static public int iString(String tag, String msg) { + if (log) + return Log.i(tag, msg); + else + return 0; + } + + static public int wString(String tag, String msg) { + if (log) + return Log.w(tag, msg); + else + return 0; + } + + static public int vString(String tag, String msg) { + if (log) + return Log.v(tag, msg); + else + return 0; + } + + static public int d(Object obj, String msg) { + return dString(obj.toString(), msg); + } + + static public int e(Object obj, String msg) { + return eString(obj.toString(), msg); + } + + static public int i(Object obj, String msg) { + return iString(obj.toString(), msg); + } + + static public int w(Object obj, String msg) { + return wString(obj.toString(), msg); + } + + static public int v(Object obj, String msg) { + return vString(obj.toString(), msg); + } + + /** + * Print stack trace if log is enabled. + * + * @param obj + * @param exception + */ + public static void exception(Object obj, Exception exception) { + if (!log) + return; + forceException(obj, exception); + } + + /** + * Print stack trace even if log is disabled. + * + * @param obj + * @param exception + */ + public static void forceException(Object obj, Exception exception) { + System.err.println(obj.toString()); + System.err.println(getStackTrace(exception)); + } + + /** + * @param exception + * @return stack trace. + */ + private static String getStackTrace(Exception exception) { + final StringWriter result = new StringWriter(); + final PrintWriter printWriter = new PrintWriter(result); + exception.printStackTrace(printWriter); + return result.toString(); + } + + public static boolean isDebugable() { + return debugable; + } } diff --git a/app/src/main/java/com/xabber/android/data/NetworkException.java b/app/src/main/java/com/xabber/android/data/NetworkException.java index 9cd45c12af..139f9d7dc5 100644 --- a/app/src/main/java/com/xabber/android/data/NetworkException.java +++ b/app/src/main/java/com/xabber/android/data/NetworkException.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -19,45 +19,44 @@ /** * Exception with specified error's string resource. - * + * * @author alexander.ivanov - * */ public class NetworkException extends Exception { - private static final long serialVersionUID = 1L; - private final int resourceId; - private final Throwable wrappedThrowable; - - public NetworkException(int resourceId) { - this(resourceId, null); - } - - public NetworkException(int resourceId, Throwable wrappedThrowable) { - super(); - this.resourceId = resourceId; - this.wrappedThrowable = wrappedThrowable; - } - - public int getResourceId() { - return resourceId; - } - - @Override - public void printStackTrace(PrintStream out) { - super.printStackTrace(out); - if (wrappedThrowable != null) { - out.println("Nested Exception: "); - wrappedThrowable.printStackTrace(out); - } - } - - @Override - public void printStackTrace(PrintWriter out) { - super.printStackTrace(out); - if (wrappedThrowable != null) { - out.println("Nested Exception: "); - wrappedThrowable.printStackTrace(out); - } - } + private static final long serialVersionUID = 1L; + private final int resourceId; + private final Throwable wrappedThrowable; + + public NetworkException(int resourceId) { + this(resourceId, null); + } + + public NetworkException(int resourceId, Throwable wrappedThrowable) { + super(); + this.resourceId = resourceId; + this.wrappedThrowable = wrappedThrowable; + } + + public int getResourceId() { + return resourceId; + } + + @Override + public void printStackTrace(PrintStream out) { + super.printStackTrace(out); + if (wrappedThrowable != null) { + out.println("Nested Exception: "); + wrappedThrowable.printStackTrace(out); + } + } + + @Override + public void printStackTrace(PrintWriter out) { + super.printStackTrace(out); + if (wrappedThrowable != null) { + out.println("Nested Exception: "); + wrappedThrowable.printStackTrace(out); + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/data/OnClearListener.java b/app/src/main/java/com/xabber/android/data/OnClearListener.java index 583323b8d9..c9157bfc22 100644 --- a/app/src/main/java/com/xabber/android/data/OnClearListener.java +++ b/app/src/main/java/com/xabber/android/data/OnClearListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,17 +16,17 @@ /** * Manager that can clear his data. - * + * * @author alexander.ivanov */ public interface OnClearListener extends BaseManagerInterface { - /** - * Clear all local data. - * - * WILL BE CALLED FROM BACKGROUND THREAD. DON'T CHANGE OR ACCESS - * APPLICATION'S DATA HERE! - */ - void onClear(); + /** + * Clear all local data. + *

+ * WILL BE CALLED FROM BACKGROUND THREAD. DON'T CHANGE OR ACCESS + * APPLICATION'S DATA HERE! + */ + void onClear(); } diff --git a/app/src/main/java/com/xabber/android/data/OnCloseListener.java b/app/src/main/java/com/xabber/android/data/OnCloseListener.java index 0bf98a405f..9289c174a2 100644 --- a/app/src/main/java/com/xabber/android/data/OnCloseListener.java +++ b/app/src/main/java/com/xabber/android/data/OnCloseListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,16 +16,15 @@ /** * Listen for application to being closing. - * + * * @author alexander.ivanov - * */ public interface OnCloseListener extends BaseManagerInterface { - /** - * Called after service have been stoped. - * - * This function will be call from UI thread. - */ - public void onClose(); + /** + * Called after service have been stoped. + *

+ * This function will be call from UI thread. + */ + void onClose(); } diff --git a/app/src/main/java/com/xabber/android/data/OnErrorListener.java b/app/src/main/java/com/xabber/android/data/OnErrorListener.java index ba5e39408e..59f024f3d0 100644 --- a/app/src/main/java/com/xabber/android/data/OnErrorListener.java +++ b/app/src/main/java/com/xabber/android/data/OnErrorListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,16 +16,15 @@ /** * Listener for the error. - * + * * @author alexander.ivanov */ interface OnErrorListener extends BaseUIListener { - /** - * Error occurred. - * - * @param resourceId - * String with error description. - */ - public void onError(int resourceId); + /** + * Error occurred. + * + * @param resourceId String with error description. + */ + void onError(int resourceId); } diff --git a/app/src/main/java/com/xabber/android/data/OnInitializedListener.java b/app/src/main/java/com/xabber/android/data/OnInitializedListener.java index ce0bc2bb8f..db254fdc17 100644 --- a/app/src/main/java/com/xabber/android/data/OnInitializedListener.java +++ b/app/src/main/java/com/xabber/android/data/OnInitializedListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,17 +16,16 @@ /** * Listen for application fully initialized. - * + * * @author alexander.ivanov - * */ public interface OnInitializedListener extends BaseManagerInterface { - /** - * Called once on service start and all data were loaded. - * - * Called from UI thread. - */ - void onInitialized(); + /** + * Called once on service start and all data were loaded. + *

+ * Called from UI thread. + */ + void onInitialized(); } diff --git a/app/src/main/java/com/xabber/android/data/OnLoadListener.java b/app/src/main/java/com/xabber/android/data/OnLoadListener.java index ca2eb24830..bd46f83c39 100644 --- a/app/src/main/java/com/xabber/android/data/OnLoadListener.java +++ b/app/src/main/java/com/xabber/android/data/OnLoadListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,21 +16,20 @@ /** * Listen for application to be ready to load data. - * + * * @author alexander.ivanov - * */ public interface OnLoadListener extends BaseManagerInterface { - /** - * Called after service has been started before - * {@link OnInitializedListener}. - * - * WILL BE CALLED FROM BACKGROUND THREAD. DON'T CHANGE OR ACCESS - * APPLICATION'S DATA HERE! - * - * Used to load data from DB and post request to UI thread to update data. - */ - void onLoad(); + /** + * Called after service has been started before + * {@link OnInitializedListener}. + *

+ * WILL BE CALLED FROM BACKGROUND THREAD. DON'T CHANGE OR ACCESS + * APPLICATION'S DATA HERE! + *

+ * Used to load data from DB and post request to UI thread to update data. + */ + void onLoad(); } diff --git a/app/src/main/java/com/xabber/android/data/OnLowMemoryListener.java b/app/src/main/java/com/xabber/android/data/OnLowMemoryListener.java index f9f300eb08..530d0ae32a 100644 --- a/app/src/main/java/com/xabber/android/data/OnLowMemoryListener.java +++ b/app/src/main/java/com/xabber/android/data/OnLowMemoryListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,15 +16,14 @@ /** * Listener for optimization request on low memory. - * + * * @author alexander.ivanov - * */ public interface OnLowMemoryListener extends BaseManagerInterface { - /** - * Clears all caches. - */ - void onLowMemory(); + /** + * Clears all caches. + */ + void onLowMemory(); } diff --git a/app/src/main/java/com/xabber/android/data/OnMigrationListener.java b/app/src/main/java/com/xabber/android/data/OnMigrationListener.java index ede44ee933..c08021b823 100644 --- a/app/src/main/java/com/xabber/android/data/OnMigrationListener.java +++ b/app/src/main/java/com/xabber/android/data/OnMigrationListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,17 +16,16 @@ /** * Listener for database migration. - * + * * @author alexander.ivanov - * */ public interface OnMigrationListener extends BaseManagerInterface { - /** - * Called on database migration for each intermediate versions. - * - * @param toVersion - */ - void onMigrate(int toVersion); + /** + * Called on database migration for each intermediate versions. + * + * @param toVersion + */ + void onMigrate(int toVersion); } diff --git a/app/src/main/java/com/xabber/android/data/OnTimerListener.java b/app/src/main/java/com/xabber/android/data/OnTimerListener.java index c1a6551b1e..6a77b93878 100644 --- a/app/src/main/java/com/xabber/android/data/OnTimerListener.java +++ b/app/src/main/java/com/xabber/android/data/OnTimerListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,22 +16,21 @@ /** * Listener for the periodically call. - * + *

* At least {@link #DELAY} milliseconds will pass before the next call. - * + *

* First time it will be called after at least {@link #DELAY} milliseconds after * {@link OnInitializedListener#onInitialized()}. - * + * * @author alexander.ivanov - * */ public interface OnTimerListener extends BaseManagerInterface { - public final static int DELAY = 1000; + int DELAY = 1000; - /** - * Called after at least {@link #DELAY} milliseconds. - */ - void onTimer(); + /** + * Called after at least {@link #DELAY} milliseconds. + */ + void onTimer(); } diff --git a/app/src/main/java/com/xabber/android/data/OnUnloadListener.java b/app/src/main/java/com/xabber/android/data/OnUnloadListener.java index f53db528b6..a20e5a409b 100644 --- a/app/src/main/java/com/xabber/android/data/OnUnloadListener.java +++ b/app/src/main/java/com/xabber/android/data/OnUnloadListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,19 +16,18 @@ /** * Listen for application to being closed. - * + * * @author alexander.ivanov - * */ public interface OnUnloadListener extends BaseManagerInterface { - /** - * Called before application to be killed after - * {@link OnCloseListener#onClose()} has been called. - * - * WILL BE CALLED FROM BACKGROUND THREAD. DON'T CHANGE OR ACCESS - * APPLICATION'S DATA HERE! - */ - public void onUnload(); + /** + * Called before application to be killed after + * {@link OnCloseListener#onClose()} has been called. + *

+ * WILL BE CALLED FROM BACKGROUND THREAD. DON'T CHANGE OR ACCESS + * APPLICATION'S DATA HERE! + */ + void onUnload(); } diff --git a/app/src/main/java/com/xabber/android/data/OnWipeListener.java b/app/src/main/java/com/xabber/android/data/OnWipeListener.java index 36f4ba6d58..04a1c79410 100644 --- a/app/src/main/java/com/xabber/android/data/OnWipeListener.java +++ b/app/src/main/java/com/xabber/android/data/OnWipeListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,17 +16,17 @@ /** * Listener for request to wipe all sensitive application data. - * + * * @author alexander.ivanov */ public interface OnWipeListener extends BaseManagerInterface { - /** - * Wipe all sensitive application data. - * - * WILL BE CALLED FROM BACKGROUND THREAD. DON'T CHANGE OR ACCESS - * APPLICATION'S DATA HERE! - */ - void onWipe(); + /** + * Wipe all sensitive application data. + *

+ * WILL BE CALLED FROM BACKGROUND THREAD. DON'T CHANGE OR ACCESS + * APPLICATION'S DATA HERE! + */ + void onWipe(); } diff --git a/app/src/main/java/com/xabber/android/data/SettingsManager.java b/app/src/main/java/com/xabber/android/data/SettingsManager.java index a10e223b78..2a1013ce61 100644 --- a/app/src/main/java/com/xabber/android/data/SettingsManager.java +++ b/app/src/main/java/com/xabber/android/data/SettingsManager.java @@ -1,24 +1,19 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data; -import java.util.Collections; -import java.util.Comparator; -import java.util.Map; -import java.util.regex.Pattern; - import android.content.Context; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; @@ -28,6 +23,7 @@ import android.provider.Settings; import android.text.TextUtils; +import com.xabber.android.R; import com.xabber.android.data.account.AccountManager; import com.xabber.android.data.account.StatusMode; import com.xabber.android.data.connection.NetworkManager; @@ -39,744 +35,720 @@ import com.xabber.android.service.XabberService; import com.xabber.android.ui.adapter.ComparatorByName; import com.xabber.android.ui.adapter.ComparatorByStatus; -import com.xabber.android.utils.Emoticons; -import com.xabber.androiddev.R; +import com.xabber.xmpp.carbon.CarbonManager; + +import java.util.Comparator; /** * Manage operations with common settings. - * + * * @author alexander.ivanov - * */ public class SettingsManager implements OnInitializedListener, - OnMigrationListener, OnSharedPreferenceChangeListener { - - public static enum ChatsHistory { - - /** - * Don't store chat messages. - */ - none, - - /** - * Store only unread messages. - */ - unread, - - /** - * Store all messages. - */ - all; - - } - - public static enum InterfaceTheme { - - /** - * All windows will be dark. - */ - dark, - - /** - * All windows will be light. - */ - light, - - /** - * Chat will be light, other windows will be dark. - */ - normal; - - } - - public static enum EventsMessage { - - /** - * Never notify. - */ - none, - - /** - * Notify in chat only. - */ - chat, + OnMigrationListener, OnSharedPreferenceChangeListener { + + private static final SettingsManager instance; + + static { + instance = new SettingsManager(); + Application.getInstance().addManager(instance); + } + + private SettingsManager() { + getSharedPreferences().registerOnSharedPreferenceChangeListener(this); + } + + public static SettingsManager getInstance() { + return instance; + } + + private static SharedPreferences getSharedPreferences() { + return PreferenceManager.getDefaultSharedPreferences(Application + .getInstance()); + } + + private static int getInt(int key, int def) { + String value = getString(key, def); + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + return Integer.parseInt(Application.getInstance().getString(def)); + } + } + + private static boolean getBoolean(int key, boolean def) { + return getSharedPreferences().getBoolean( + Application.getInstance().getString(key), def); + } + + private static boolean getBoolean(int key, int def) { + return getBoolean(key, Application.getInstance().getResources() + .getBoolean(def)); + } + + private static void setBoolean(int key, boolean value) { + Editor editor = getSharedPreferences().edit(); + editor.putBoolean(Application.getInstance().getString(key), value); + editor.commit(); + } + + private static String getString(int key, String def) { + return getSharedPreferences().getString( + Application.getInstance().getString(key), def); + } + + private static String getString(int key, int def) { + return getString(key, Application.getInstance().getString(def)); + } + + private static void setString(int key, String value) { + Editor editor = getSharedPreferences().edit(); + editor.putString(Application.getInstance().getString(key), value); + editor.commit(); + } + + /** + * @param key + * @param defaultUri + * @return Sound uri. Sets defaulUri if value is default. + */ + private static Uri getSound(int key, Uri defaultUri, int defaultResource) { + String defaultValue = Application.getInstance().getString( + defaultResource); + String value = getString(key, defaultValue); + if (TextUtils.isEmpty(value)) + return null; + if (defaultValue.equals(value)) { + setString(key, defaultUri.toString()); + return defaultUri; + } + return Uri.parse(value); + } + + public static boolean contactsShowAvatars() { + return getBoolean(R.string.contacts_show_avatars_key, + R.bool.contacts_show_avatars_default); + } + + public static boolean contactsShowOffline() { + return getBoolean(R.string.contacts_show_offline_key, + R.bool.contacts_show_offline_default); + } + + public static void setContactsShowOffline(boolean show) { + setBoolean(R.string.contacts_show_offline_key, show); + } + + public static boolean contactsShowGroups() { + return getBoolean(R.string.contacts_show_groups_key, + R.bool.contacts_show_groups_default); + } + + public static boolean contactsShowEmptyGroups() { + return getBoolean(R.string.contacts_show_empty_groups_key, + R.bool.contacts_show_empty_groups_default); + } + + public static boolean contactsShowActiveChats() { + return getBoolean(R.string.contacts_show_active_chats_key, + R.bool.contacts_show_active_chats_default); + } + + public static boolean contactsStayActiveChats() { + return getBoolean(R.string.contacts_stay_active_chats_key, + R.bool.contacts_stay_active_chats_default); + } + + public static boolean contactsShowAccounts() { + return getBoolean(R.string.contacts_show_accounts_key, + R.bool.contacts_show_accounts_default); + } + + public static Comparator contactsOrder() { + String value = getString(R.string.contacts_order_key, + R.string.contacts_order_default); + if (Application.getInstance() + .getString(R.string.contacts_order_alphabet_value) + .equals(value)) + return ComparatorByName.COMPARATOR_BY_NAME; + else if (Application.getInstance() + .getString(R.string.contacts_order_status_value).equals(value)) + return ComparatorByStatus.COMPARATOR_BY_STATUS; + else + throw new IllegalStateException(); + } + + public static boolean contactsShowPanel() { + return getBoolean(R.string.contacts_show_panel_key, + R.bool.contacts_show_panel_default); + } + + public static boolean contactsEnableShowAccounts() { + return getBoolean(R.string.contacts_enable_show_accounts_key, + R.bool.contacts_enable_show_accounts_default); + } + + /** + * DON`T USE THIS METHOD DIRECTLY. + *

+ * Use {@link AccountManager#getSelectedAccount()} instead. + * + * @return + */ + public static String contactsSelectedAccount() { + return getString(R.string.contacts_selected_account_key, ""); + } + + public static void setContactsSelectedAccount(String value) { + if (value == null) + value = ""; + setString(R.string.contacts_selected_account_key, value); + } + + public static void enableContactsShowAccount() { + setBoolean(R.string.contacts_enable_show_accounts_key, false); + setBoolean(R.string.contacts_show_accounts_key, true); + } + + /** + * Gets event sound. It will save DEFAULT_NOTIFICATION_URI if value is + * default. + * + * @return {@link Uri} or null. + */ + public static Uri eventsSound() { + return getSound(R.string.events_sound_key, + Settings.System.DEFAULT_NOTIFICATION_URI, + R.string.events_sound_default); + } + + public static boolean eventsVibro() { + return getBoolean(R.string.events_vibro_key, + R.bool.events_vibro_default); + } + + public static boolean eventsSuppress100() { + return getBoolean(R.string.chat_events_suppress_100_key, + R.bool.chat_events_suppress_100_default); + } + + public static boolean eventsIgnoreSystemVibro() { + return getBoolean(R.string.events_ignore_system_vibro_key, + R.bool.events_ignore_system_vibro_default); + } + + public static boolean eventsLightning() { + return getBoolean(R.string.events_lightning_key, + R.bool.events_lightning_default); + } + + public static boolean eventsPersistent() { + return getBoolean(R.string.events_persistent_key, + R.bool.events_persistent_default); + } + + public static boolean eventsShowText() { + return getBoolean(R.string.events_show_text_key, + R.bool.events_show_text_default); + } + + public static EventsMessage eventsMessage() { + String value = getString(R.string.events_message_key, + R.string.events_message_default); + if (Application.getInstance() + .getString(R.string.events_message_none_value).equals(value)) + return EventsMessage.none; + else if (Application.getInstance() + .getString(R.string.events_message_chat_value).equals(value)) + return EventsMessage.chat; + else if (Application.getInstance() + .getString(R.string.events_message_chat_and_muc_value) + .equals(value)) + return EventsMessage.chatAndMuc; + else + throw new IllegalStateException(); + } + + public static boolean eventsVisibleChat() { + return getBoolean(R.string.events_visible_chat_key, + R.bool.events_visible_chat_default); + } + + public static boolean eventsFirstOnly() { + return getBoolean(R.string.events_first_only_key, + R.bool.events_first_only_default); + } + + public static boolean chatsShowAvatars() { + return getBoolean(R.string.chats_show_avatars_key, + R.bool.chats_show_avatars_default); + } + + public static boolean chatsSendByEnter() { + return getBoolean(R.string.chats_send_by_enter_key, + R.bool.chats_send_by_enter_default); + } + + public static ChatsShowStatusChange chatsShowStatusChange() { + String value = getString(R.string.chats_show_status_change_key, + R.string.chats_show_status_change_default); + if (Application.getInstance() + .getString(R.string.chats_show_status_change_always_value) + .equals(value)) + return ChatsShowStatusChange.always; + else if (Application.getInstance() + .getString(R.string.chats_show_status_change_muc_value) + .equals(value)) + return ChatsShowStatusChange.muc; + else if (Application.getInstance() + .getString(R.string.chats_show_status_change_never_value) + .equals(value)) + return ChatsShowStatusChange.never; + else + throw new IllegalStateException(); + + } + + public static ChatsHideKeyboard chatsHideKeyboard() { + String value = getString(R.string.chats_hide_keyboard_key, + R.string.chats_hide_keyboard_default); + if (Application.getInstance() + .getString(R.string.chats_hide_keyboard_always_value) + .equals(value)) + return ChatsHideKeyboard.always; + else if (Application.getInstance() + .getString(R.string.chats_hide_keyboard_landscape_value) + .equals(value)) + return ChatsHideKeyboard.landscape; + else if (Application.getInstance() + .getString(R.string.chats_hide_keyboard_never_value) + .equals(value)) + return ChatsHideKeyboard.never; + else + throw new IllegalStateException(); + } + + public static boolean chatsShowBackground() { + return getBoolean(R.string.chats_show_background_key, R.bool.chats_show_background_default); + } + + public static int chatsAppearanceStyle() { + String value = getString(R.string.chats_font_size_key, + R.string.chats_font_size_default); + if (Application.getInstance() + .getString(R.string.chats_font_size_small_value).equals(value)) + return R.style.ChatText_Small; + else if (Application.getInstance() + .getString(R.string.chats_font_size_normal_value).equals(value)) + return R.style.ChatText_Normal; + else if (Application.getInstance() + .getString(R.string.chats_font_size_large_value).equals(value)) + return R.style.ChatText_Large; + else if (Application.getInstance() + .getString(R.string.chats_font_size_xlarge_value).equals(value)) + return R.style.ChatText_XLarge; + else + throw new IllegalStateException(); + } + + public static boolean chatsStateNotification() { + return getBoolean(R.string.chats_state_notification_key, + R.bool.chats_state_notification_default); + } + + public static boolean chatsAttention() { + return getBoolean(R.string.chats_attention_key, + R.bool.chats_attention_default); + } + + /** + * Gets event sound. It will save DEFAULT_NOTIFICATION_URI if value is + * default. + * + * @return {@link Uri} or null. + */ + public static Uri chatsAttentionSound() { + return getSound(R.string.chats_attention_sound_key, + Settings.System.DEFAULT_RINGTONE_URI, + R.string.chats_attention_sound_default); + } + + public static int connectionGoAway() { + return getInt(R.string.connection_go_away_key, + R.string.connection_go_away_default); + } + + public static int connectionGoXa() { + return getInt(R.string.connection_go_xa_key, + R.string.connection_go_xa_default); + } + + public static boolean connectionWifiLock() { + return getBoolean(R.string.connection_wifi_lock_key, + R.bool.connection_wifi_lock_default); + } + + public static boolean connectionWakeLock() { + return getBoolean(R.string.connection_wake_lock_key, + R.bool.connection_wake_lock_default); + } + + public static boolean connectionStartAtBoot() { + return getBoolean(R.string.connection_start_at_boot_key, + R.bool.connection_start_at_boot_default); + } + + public static void setConnectionStartAtBoot(boolean value) { + setBoolean(R.string.connection_start_at_boot_key, value); + } + + public static boolean connectionLoadVCard() { + return getBoolean(R.string.connection_load_vcard_key, + R.bool.connection_load_vcard_default); + } + + public static boolean connectionUseCarbons() { + return getBoolean(R.string.connection_use_carbons_key, + R.bool.connection_use_carbons_default); + } + + public static boolean connectionAdjustPriority() { + return getBoolean(R.string.connection_adjust_priority_key, + R.bool.connection_adjust_priority_default); + } + + public static int connectionPriorityAvailable() { + return getInt(R.string.connection_priority_available_key, + R.string.connection_priority_available_default); + } + + public static int connectionPriorityAway() { + return getInt(R.string.connection_priority_away_key, + R.string.connection_priority_away_default); + } + + public static int connectionPriorityChat() { + return getInt(R.string.connection_priority_chat_key, + R.string.connection_priority_chat_default); + } + + public static int connectionPriorityDnd() { + return getInt(R.string.connection_priority_dnd_key, + R.string.connection_priority_dnd_default); + } + + public static int connectionPriorityXa() { + return getInt(R.string.connection_priority_xa_key, + R.string.connection_priority_xa_default); + } + + public static boolean debugLog() { + return getBoolean(R.string.debug_log_key, R.bool.debug_log_default); + } + + public static InterfaceTheme interfaceTheme() { + String value = getString(R.string.interface_theme_key, + R.string.interface_theme_default); + if (Application.getInstance() + .getString(R.string.interface_theme_dark_value).equals(value)) + return InterfaceTheme.dark; + else if (Application.getInstance() + .getString(R.string.interface_theme_light_value).equals(value)) + return InterfaceTheme.light; + else if (Application.getInstance() + .getString(R.string.interface_theme_normal_value).equals(value)) + return InterfaceTheme.normal; + else + throw new IllegalStateException(); + } + + public static boolean securityCheckCertificate() { + return getBoolean(R.string.security_check_certificate_key, + R.bool.security_check_certificate_default); + } + + public static SecurityOtrMode securityOtrMode() { + String value = getString(R.string.security_otr_mode_key, + R.string.security_otr_mode_default); + if (Application.getInstance() + .getString(R.string.security_otr_mode_disabled_value) + .equals(value)) + return SecurityOtrMode.disabled; + else if (Application.getInstance() + .getString(R.string.security_otr_mode_manual_value) + .equals(value)) + return SecurityOtrMode.manual; + else if (Application.getInstance() + .getString(R.string.security_otr_mode_auto_value).equals(value)) + return SecurityOtrMode.auto; + else if (Application.getInstance() + .getString(R.string.security_otr_mode_required_value) + .equals(value)) + return SecurityOtrMode.required; + else + throw new IllegalStateException(); + } + + public static boolean securityOtrHistory() { + return getBoolean(R.string.security_otr_history_key, + R.bool.security_otr_history_default); + } + + public static int bootCount() { + return getSharedPreferences() + .getInt(Application.getInstance().getString( + R.string.boot_count_key), 0); + } + + public static void incrementBootCount() { + Editor editor = getSharedPreferences().edit(); + editor.putInt( + Application.getInstance().getString(R.string.boot_count_key), + bootCount() + 1); + editor.commit(); + } + + public static boolean startAtBootSuggested() { + return getBoolean(R.string.start_at_boot_suggested_key, false); + } + + public static void setStartAtBootSuggested() { + setBoolean(R.string.start_at_boot_suggested_key, true); + } + + public static boolean contactIntegrationSuggested() { + return getBoolean(R.string.contact_integration_suggested_key, false); + } + + public static void setContactIntegrationSuggested() { + setBoolean(R.string.contact_integration_suggested_key, true); + } + + public static boolean isTranslationSuggested() { + return getBoolean(R.string.translation_suggested_key, false); + } + + public static void setTranslationSuggested() { + setBoolean(R.string.translation_suggested_key, true); + } + + /** + * @return Common status mode for all accounts or + * {@link StatusMode#available} if mode was not set. + */ + public static StatusMode statusMode() { + return StatusMode.valueOf(getString(R.string.status_mode_key, + StatusMode.available.name())); + } + + public static void setStatusMode(StatusMode statusMode) { + setString(R.string.status_mode_key, statusMode.name()); + } + + /** + * @return Common status text for all accounts. + */ + public static String statusText() { + return getString(R.string.status_text_key, ""); + } + + public static void setStatusText(String statusText) { + setString(R.string.status_text_key, statusText); + } + + @Override + public void onInitialized() { + incrementBootCount(); + } + + @Override + public void onMigrate(int toVersion) { + switch (toVersion) { + case 32: + setBoolean(R.string.chats_show_status_change_key, false); + break; + case 40: + String value; + try { + if (getBoolean(R.string.chats_show_status_change_key, false)) + value = Application.getInstance().getString( + R.string.chats_show_status_change_always_value); + else + value = Application.getInstance().getString( + R.string.chats_show_status_change_muc_value); + } catch (ClassCastException e) { + value = Application.getInstance().getString( + R.string.chats_show_status_change_default); + } + setString(R.string.chats_show_status_change_key, value); + break; + case 45: + setBoolean(R.string.chats_show_avatars_key, + "message".equals(getString(R.string.chats_show_avatars_key, + ""))); + break; + case 65: + SharedPreferences settings = Application.getInstance() + .getSharedPreferences("accounts", Context.MODE_PRIVATE); + int statusModeIndex = settings.getInt("status_mode", + StatusMode.available.ordinal()); + StatusMode statusMode = StatusMode.values()[statusModeIndex]; + setString(R.string.status_mode_key, statusMode.name()); + String statusText = settings.getString("status_text", ""); + setString(R.string.status_text_key, statusText); + break; + default: + break; + } + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, + String key) { + if (key.equals(Application.getInstance().getString( + R.string.chats_show_status_change_key))) { + MessageManager.getInstance().onSettingsChanged(); + } else if (key.equals(Application.getInstance().getString( + R.string.events_persistent_key))) { + NotificationManager.getInstance().onMessageNotification(); + XabberService.getInstance().changeForeground(); + } else if (key.equals(Application.getInstance().getString( + R.string.connection_wake_lock_key))) { + NetworkManager.getInstance().onWakeLockSettingsChanged(); + } else if (key.equals(Application.getInstance().getString( + R.string.connection_wifi_lock_key))) { + NetworkManager.getInstance().onWifiLockSettingsChanged(); + } else if (key.equals(Application.getInstance().getString( + R.string.connection_use_carbons_key))) { + CarbonManager.getInstance().onUseCarbonsSettingsChanged(); + } else if (key.equals(Application.getInstance().getString( + R.string.events_show_text_key))) { + NotificationManager.getInstance().onMessageNotification(); + } else if (key.equals(Application.getInstance().getString( + R.string.chats_attention_key))) { + AttentionManager.getInstance().onSettingsChanged(); + } else if (key.equals(Application.getInstance().getString( + R.string.security_otr_mode_key))) { + OTRManager.getInstance().onSettingsChanged(); + } + } + + public enum ChatsHistory { + + /** + * Don't store chat messages. + */ + none, + + /** + * Store only unread messages. + */ + unread, + + /** + * Store all messages. + */ + all + + } + + public enum InterfaceTheme { + + /** + * All windows will be dark. + */ + dark, + + /** + * All windows will be light. + */ + light, + + /** + * Chat will be light, other windows will be dark. + */ + normal + + } + + public enum EventsMessage { + + /** + * Never notify. + */ + none, + + /** + * Notify in chat only. + */ + chat, + + /** + * Notify in chat and muc. + */ + chatAndMuc + + } + + public enum ChatsShowStatusChange { + + /** + * Always show status change. + */ + always, + + /** + * Show status change only in MUC. + */ + muc, + + /** + * Never show status change. + */ + never + + } + + public enum ChatsHideKeyboard { + + /** + * Always hide keyboard. + */ + always, + + /** + * Hide keyboard only in landscape mode. + */ + landscape, + + /** + * Never hide keyboard. + */ + never, + } - /** - * Notify in chat and muc. - */ - chatAndMuc; + public enum SecurityOtrMode { - }; + /** + * OTR is disabled. + */ + disabled, - public enum ChatsShowStatusChange { + /** + * Manually send request and confirm requests. + */ + manual, - /** - * Always show status change. - */ - always, + /** + * Automatically try to use OTR. + */ + auto, - /** - * Show status change only in MUC. - */ - muc, + /** + * Require to use OTR. + */ + required - /** - * Never show status change. - */ - never; - - } + } - public enum ChatsHideKeyboard { - - /** - * Always hide keyboard. - */ - always, - - /** - * Hide keyboard only in landscape mode. - */ - landscape, - - /** - * Never hide keyboard. - */ - never, - } - - public enum ChatsDivide { - - /** - * Always divide message header from text. - */ - always, - - /** - * Only in portial mode. - */ - portial, - - /** - * Never. - */ - never; - - } - - public enum SecurityOtrMode { - - /** - * OTR is disabled. - */ - disabled, - - /** - * Manually send request and confirm requests. - */ - manual, - - /** - * Automatically try to use OTR. - */ - auto, - - /** - * Require to use OTR. - */ - required; - - } - - private static final SettingsManager instance; - - static { - instance = new SettingsManager(); - Application.getInstance().addManager(instance); - } - - public static SettingsManager getInstance() { - return instance; - } - - private SettingsManager() { - getSharedPreferences().registerOnSharedPreferenceChangeListener(this); - } - - @Override - public void onInitialized() { - incrementBootCount(); - } - - private static SharedPreferences getSharedPreferences() { - return PreferenceManager.getDefaultSharedPreferences(Application - .getInstance()); - } - - private static int getInt(int key, int def) { - String value = getString(key, def); - try { - return Integer.parseInt(value); - } catch (NumberFormatException e) { - return Integer.parseInt(Application.getInstance().getString(def)); - } - } - - private static boolean getBoolean(int key, boolean def) { - return getSharedPreferences().getBoolean( - Application.getInstance().getString(key), def); - } - - private static boolean getBoolean(int key, int def) { - return getBoolean(key, Application.getInstance().getResources() - .getBoolean(def)); - } - - private static void setBoolean(int key, boolean value) { - Editor editor = getSharedPreferences().edit(); - editor.putBoolean(Application.getInstance().getString(key), value); - editor.commit(); - } - - private static String getString(int key, String def) { - return getSharedPreferences().getString( - Application.getInstance().getString(key), def); - } - - private static String getString(int key, int def) { - return getString(key, Application.getInstance().getString(def)); - } - - private static void setString(int key, String value) { - Editor editor = getSharedPreferences().edit(); - editor.putString(Application.getInstance().getString(key), value); - editor.commit(); - } - - /** - * @param key - * @param defaultUri - * @return Sound uri. Sets defaulUri if value is default. - */ - private static Uri getSound(int key, Uri defaultUri, int defaultResource) { - String defaultValue = Application.getInstance().getString( - defaultResource); - String value = getString(key, defaultValue); - if (TextUtils.isEmpty(value)) - return null; - if (defaultValue.equals(value)) { - setString(key, defaultUri.toString()); - return defaultUri; - } - return Uri.parse(value); - } - - public static boolean contactsShowAvatars() { - return getBoolean(R.string.contacts_show_avatars_key, - R.bool.contacts_show_avatars_default); - } - - public static boolean contactsShowOffline() { - return getBoolean(R.string.contacts_show_offline_key, - R.bool.contacts_show_offline_default); - } - - public static void setContactsShowOffline(boolean show) { - setBoolean(R.string.contacts_show_offline_key, show); - } - - public static boolean contactsShowGroups() { - return getBoolean(R.string.contacts_show_groups_key, - R.bool.contacts_show_groups_default); - } - - public static boolean contactsShowEmptyGroups() { - return getBoolean(R.string.contacts_show_empty_groups_key, - R.bool.contacts_show_empty_groups_default); - } - - public static boolean contactsShowActiveChats() { - return getBoolean(R.string.contacts_show_active_chats_key, - R.bool.contacts_show_active_chats_default); - } - - public static boolean contactsStayActiveChats() { - return getBoolean(R.string.contacts_stay_active_chats_key, - R.bool.contacts_stay_active_chats_default); - } - - public static boolean contactsShowAccounts() { - return getBoolean(R.string.contacts_show_accounts_key, - R.bool.contacts_show_accounts_default); - } - - public static Comparator contactsOrder() { - String value = getString(R.string.contacts_order_key, - R.string.contacts_order_default); - if (Application.getInstance() - .getString(R.string.contacts_order_alphabet_value) - .equals(value)) - return ComparatorByName.COMPARATOR_BY_NAME; - else if (Application.getInstance() - .getString(R.string.contacts_order_status_value).equals(value)) - return ComparatorByStatus.COMPARATOR_BY_STATUS; - else - throw new IllegalStateException(); - } - - public static boolean contactsShowPanel() { - return getBoolean(R.string.contacts_show_panel_key, - R.bool.contacts_show_panel_default); - } - - public static boolean contactsEnableShowAccounts() { - return getBoolean(R.string.contacts_enable_show_accounts_key, - R.bool.contacts_enable_show_accounts_default); - } - - /** - * DON`T USE THIS METHOD DIRECTLY. - * - * Use {@link AccountManager#getSelectedAccount()} instead. - * - * @return - */ - public static String contactsSelectedAccount() { - return getString(R.string.contacts_selected_account_key, ""); - } - - public static void setContactsSelectedAccount(String value) { - if (value == null) - value = ""; - setString(R.string.contacts_selected_account_key, value); - } - - public static void enableContactsShowAccount() { - setBoolean(R.string.contacts_enable_show_accounts_key, false); - setBoolean(R.string.contacts_show_accounts_key, true); - } - - /** - * Gets event sound. It will save DEFAULT_NOTIFICATION_URI if value is - * default. - * - * @return {@link Uri} or null. - */ - public static Uri eventsSound() { - return getSound(R.string.events_sound_key, - Settings.System.DEFAULT_NOTIFICATION_URI, - R.string.events_sound_default); - } - - public static boolean eventsVibro() { - return getBoolean(R.string.events_vibro_key, - R.bool.events_vibro_default); - } - - public static boolean eventsIgnoreSystemVibro() { - return getBoolean(R.string.events_ignore_system_vibro_key, - R.bool.events_ignore_system_vibro_default); - } - - public static boolean eventsLightning() { - return getBoolean(R.string.events_lightning_key, - R.bool.events_lightning_default); - } - - public static boolean eventsPersistent() { - return getBoolean(R.string.events_persistent_key, - R.bool.events_persistent_default); - } - - public static boolean eventsShowText() { - return getBoolean(R.string.events_show_text_key, - R.bool.events_show_text_default); - } - - public static EventsMessage eventsMessage() { - String value = getString(R.string.events_message_key, - R.string.events_message_default); - if (Application.getInstance() - .getString(R.string.events_message_none_value).equals(value)) - return EventsMessage.none; - else if (Application.getInstance() - .getString(R.string.events_message_chat_value).equals(value)) - return EventsMessage.chat; - else if (Application.getInstance() - .getString(R.string.events_message_chat_and_muc_value) - .equals(value)) - return EventsMessage.chatAndMuc; - else - throw new IllegalStateException(); - } - - public static boolean eventsVisibleChat() { - return getBoolean(R.string.events_visible_chat_key, - R.bool.events_visible_chat_default); - } - - public static boolean eventsFirstOnly() { - return getBoolean(R.string.events_first_only_key, - R.bool.events_first_only_default); - } - - public static boolean chatsShowAvatars() { - return getBoolean(R.string.chats_show_avatars_key, - R.bool.chats_show_avatars_default); - } - - public static boolean chatsSendByEnter() { - return getBoolean(R.string.chats_send_by_enter_key, - R.bool.chats_send_by_enter_default); - } - - public static ChatsShowStatusChange chatsShowStatusChange() { - String value = getString(R.string.chats_show_status_change_key, - R.string.chats_show_status_change_default); - if (Application.getInstance() - .getString(R.string.chats_show_status_change_always_value) - .equals(value)) - return ChatsShowStatusChange.always; - else if (Application.getInstance() - .getString(R.string.chats_show_status_change_muc_value) - .equals(value)) - return ChatsShowStatusChange.muc; - else if (Application.getInstance() - .getString(R.string.chats_show_status_change_never_value) - .equals(value)) - return ChatsShowStatusChange.never; - else - throw new IllegalStateException(); - - } - - public static ChatsHideKeyboard chatsHideKeyboard() { - String value = getString(R.string.chats_hide_keyboard_key, - R.string.chats_hide_keyboard_default); - if (Application.getInstance() - .getString(R.string.chats_hide_keyboard_always_value) - .equals(value)) - return ChatsHideKeyboard.always; - else if (Application.getInstance() - .getString(R.string.chats_hide_keyboard_landscape_value) - .equals(value)) - return ChatsHideKeyboard.landscape; - else if (Application.getInstance() - .getString(R.string.chats_hide_keyboard_never_value) - .equals(value)) - return ChatsHideKeyboard.never; - else - throw new IllegalStateException(); - } - - public static int chatsAppearanceStyle() { - String value = getString(R.string.chats_font_size_key, - R.string.chats_font_size_default); - if (Application.getInstance() - .getString(R.string.chats_font_size_small_value).equals(value)) - return R.style.ChatText_Small; - else if (Application.getInstance() - .getString(R.string.chats_font_size_normal_value).equals(value)) - return R.style.ChatText_Normal; - else if (Application.getInstance() - .getString(R.string.chats_font_size_large_value).equals(value)) - return R.style.ChatText_Large; - else if (Application.getInstance() - .getString(R.string.chats_font_size_xlarge_value).equals(value)) - return R.style.ChatText_XLarge; - else - throw new IllegalStateException(); - } - - public static ChatsDivide chatsDivide() { - String value = getString(R.string.chats_divide_key, - R.string.chats_divide_default); - if (Application.getInstance() - .getString(R.string.chats_divide_always_value).equals(value)) - return ChatsDivide.always; - else if (Application.getInstance() - .getString(R.string.chats_divide_portrait_value).equals(value)) - return ChatsDivide.portial; - else if (Application.getInstance() - .getString(R.string.chats_divide_never_value).equals(value)) - return ChatsDivide.never; - else - throw new IllegalStateException(); - } - - public static boolean chatsStateNotification() { - return getBoolean(R.string.chats_state_notification_key, - R.bool.chats_state_notification_default); - } - - public static boolean chatsAttention() { - return getBoolean(R.string.chats_attention_key, - R.bool.chats_attention_default); - } - - /** - * Gets event sound. It will save DEFAULT_NOTIFICATION_URI if value is - * default. - * - * @return {@link Uri} or null. - */ - public static Uri chatsAttentionSound() { - return getSound(R.string.chats_attention_sound_key, - Settings.System.DEFAULT_RINGTONE_URI, - R.string.chats_attention_sound_default); - } - - public static int connectionGoAway() { - return getInt(R.string.connection_go_away_key, - R.string.connection_go_away_default); - } - - public static int connectionGoXa() { - return getInt(R.string.connection_go_xa_key, - R.string.connection_go_xa_default); - } - - public static boolean connectionWifiLock() { - return getBoolean(R.string.connection_wifi_lock_key, - R.bool.connection_wifi_lock_default); - } - - public static boolean connectionWakeLock() { - return getBoolean(R.string.connection_wake_lock_key, - R.bool.connection_wake_lock_default); - } - - public static boolean connectionStartAtBoot() { - return getBoolean(R.string.connection_start_at_boot_key, - R.bool.connection_start_at_boot_default); - } - - public static void setConnectionStartAtBoot(boolean value) { - setBoolean(R.string.connection_start_at_boot_key, value); - } - - public static boolean connectionLoadVCard() { - return getBoolean(R.string.connection_load_vcard_key, - R.bool.connection_load_vcard_default); - } - - public static boolean connectionAdjustPriority() { - return getBoolean(R.string.connection_adjust_priority_key, - R.bool.connection_adjust_priority_default); - } - - public static int connectionPriorityAvailable() { - return getInt(R.string.connection_priority_available_key, - R.string.connection_priority_available_default); - } - - public static int connectionPriorityAway() { - return getInt(R.string.connection_priority_away_key, - R.string.connection_priority_away_default); - } - - public static int connectionPriorityChat() { - return getInt(R.string.connection_priority_chat_key, - R.string.connection_priority_chat_default); - } - - public static int connectionPriorityDnd() { - return getInt(R.string.connection_priority_dnd_key, - R.string.connection_priority_dnd_default); - } - - public static int connectionPriorityXa() { - return getInt(R.string.connection_priority_xa_key, - R.string.connection_priority_xa_default); - } - - public static boolean debugLog() { - return getBoolean(R.string.debug_log_key, R.bool.debug_log_default); - } - - public static InterfaceTheme interfaceTheme() { - String value = getString(R.string.interface_theme_key, - R.string.interface_theme_default); - if (Application.getInstance() - .getString(R.string.interface_theme_dark_value).equals(value)) - return InterfaceTheme.dark; - else if (Application.getInstance() - .getString(R.string.interface_theme_light_value).equals(value)) - return InterfaceTheme.light; - else if (Application.getInstance() - .getString(R.string.interface_theme_normal_value).equals(value)) - return InterfaceTheme.normal; - else - throw new IllegalStateException(); - } - - public static Map interfaceSmiles() { - String value = getString(R.string.interface_smiles_key, - R.string.interface_smiles_default); - if (Application.getInstance() - .getString(R.string.interface_smiles_none_value).equals(value)) - return Collections.unmodifiableMap(Emoticons.NONE_EMOTICONS); - else if (Application.getInstance() - .getString(R.string.interface_smiles_android_value) - .equals(value)) - return Collections.unmodifiableMap(Emoticons.ANDROID_EMOTICONS); - else - throw new IllegalStateException(); - } - - public static boolean securityCheckCertificate() { - return getBoolean(R.string.security_check_certificate_key, - R.bool.security_check_certificate_default); - } - - public static SecurityOtrMode securityOtrMode() { - String value = getString(R.string.security_otr_mode_key, - R.string.security_otr_mode_default); - if (Application.getInstance() - .getString(R.string.security_otr_mode_disabled_value) - .equals(value)) - return SecurityOtrMode.disabled; - else if (Application.getInstance() - .getString(R.string.security_otr_mode_manual_value) - .equals(value)) - return SecurityOtrMode.manual; - else if (Application.getInstance() - .getString(R.string.security_otr_mode_auto_value).equals(value)) - return SecurityOtrMode.auto; - else if (Application.getInstance() - .getString(R.string.security_otr_mode_required_value) - .equals(value)) - return SecurityOtrMode.required; - else - throw new IllegalStateException(); - } - - public static boolean securityOtrHistory() { - return getBoolean(R.string.security_otr_history_key, - R.bool.security_otr_history_default); - } - - public static int bootCount() { - return getSharedPreferences() - .getInt(Application.getInstance().getString( - R.string.boot_count_key), 0); - } - - public static void incrementBootCount() { - Editor editor = getSharedPreferences().edit(); - editor.putInt( - Application.getInstance().getString(R.string.boot_count_key), - bootCount() + 1); - editor.commit(); - } - - public static boolean startAtBootSuggested() { - return getBoolean(R.string.start_at_boot_suggested_key, false); - } - - public static void setStartAtBootSuggested() { - setBoolean(R.string.start_at_boot_suggested_key, true); - } - - public static boolean contactIntegrationSuggested() { - return getBoolean(R.string.contact_integration_suggested_key, false); - } - - public static void setContactIntegrationSuggested() { - setBoolean(R.string.contact_integration_suggested_key, true); - } - - /** - * @return Common status mode for all accounts or - * {@link StatusMode#available} if mode was not set. - */ - public static StatusMode statusMode() { - return StatusMode.valueOf(getString(R.string.status_mode_key, - StatusMode.available.name())); - } - - public static void setStatusMode(StatusMode statusMode) { - setString(R.string.status_mode_key, statusMode.name()); - } - - /** - * @return Common status text for all accounts. - */ - public static String statusText() { - return getString(R.string.status_text_key, ""); - } - - public static void setStatusText(String statusText) { - setString(R.string.status_text_key, statusText); - } - - @Override - public void onMigrate(int toVersion) { - switch (toVersion) { - case 32: - setBoolean(R.string.chats_show_status_change_key, false); - break; - case 40: - String value; - try { - if (getBoolean(R.string.chats_show_status_change_key, false)) - value = Application.getInstance().getString( - R.string.chats_show_status_change_always_value); - else - value = Application.getInstance().getString( - R.string.chats_show_status_change_muc_value); - } catch (ClassCastException e) { - value = Application.getInstance().getString( - R.string.chats_show_status_change_default); - } - setString(R.string.chats_show_status_change_key, value); - break; - case 45: - setBoolean(R.string.chats_show_avatars_key, - "message".equals(getString(R.string.chats_show_avatars_key, - ""))); - break; - case 65: - SharedPreferences settings = Application.getInstance() - .getSharedPreferences("accounts", Context.MODE_PRIVATE); - int statusModeIndex = settings.getInt("status_mode", - StatusMode.available.ordinal()); - StatusMode statusMode = StatusMode.values()[statusModeIndex]; - setString(R.string.status_mode_key, statusMode.name()); - String statusText = settings.getString("status_text", ""); - setString(R.string.status_text_key, statusText); - break; - default: - break; - } - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, - String key) { - if (key.equals(Application.getInstance().getString( - R.string.chats_show_status_change_key))) { - MessageManager.getInstance().onSettingsChanged(); - } else if (key.equals(Application.getInstance().getString( - R.string.events_persistent_key))) { - NotificationManager.getInstance().onMessageNotification(); - XabberService.getInstance().changeForeground(); - } else if (key.equals(Application.getInstance().getString( - R.string.connection_wake_lock_key))) { - NetworkManager.getInstance().onWakeLockSettingsChanged(); - } else if (key.equals(Application.getInstance().getString( - R.string.connection_wifi_lock_key))) { - NetworkManager.getInstance().onWifiLockSettingsChanged(); - } else if (key.equals(Application.getInstance().getString( - R.string.events_show_text_key))) { - NotificationManager.getInstance().onMessageNotification(); - } else if (key.equals(Application.getInstance().getString( - R.string.chats_attention_key))) { - AttentionManager.getInstance().onSettingsChanged(); - } else if (key.equals(Application.getInstance().getString( - R.string.security_otr_mode_key))) { - OTRManager.getInstance().onSettingsChanged(); - } - } - -} +} \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/data/VcardMaps.java b/app/src/main/java/com/xabber/android/data/VcardMaps.java new file mode 100644 index 0000000000..3bd1c1ea3a --- /dev/null +++ b/app/src/main/java/com/xabber/android/data/VcardMaps.java @@ -0,0 +1,90 @@ +package com.xabber.android.data; + +import com.xabber.android.R; +import com.xabber.xmpp.vcard.AddressProperty; +import com.xabber.xmpp.vcard.AddressType; +import com.xabber.xmpp.vcard.EmailType; +import com.xabber.xmpp.vcard.TelephoneType; + +import java.util.HashMap; +import java.util.Map; + +/** + * Created by grigory.fedorov on 13.04.15. + */ +public class VcardMaps { + + private static final Map ADDRESS_TYPE_MAP = new HashMap<>(); + private static final Map ADDRESS_PROPERTY_MAP = new HashMap<>(); + private static final Map TELEPHONE_TYPE_MAP = new HashMap<>(); + private static final Map EMAIL_TYPE_MAP = new HashMap<>(); + + static { + ADDRESS_TYPE_MAP.put(AddressType.DOM, R.string.vcard_type_dom); + ADDRESS_TYPE_MAP.put(AddressType.HOME, R.string.vcard_type_home); + ADDRESS_TYPE_MAP.put(AddressType.INTL, R.string.vcard_type_intl); + ADDRESS_TYPE_MAP.put(AddressType.PARCEL, R.string.vcard_type_parcel); + ADDRESS_TYPE_MAP.put(AddressType.POSTAL, R.string.vcard_type_postal); + ADDRESS_TYPE_MAP.put(AddressType.PREF, R.string.vcard_type_pref); + ADDRESS_TYPE_MAP.put(AddressType.WORK, R.string.vcard_type_work); + if (ADDRESS_TYPE_MAP.size() != AddressType.values().length) + throw new IllegalStateException(); + + ADDRESS_PROPERTY_MAP.put(AddressProperty.CTRY, + R.string.vcard_address_ctry); + ADDRESS_PROPERTY_MAP.put(AddressProperty.EXTADR, + R.string.vcard_address_extadr); + ADDRESS_PROPERTY_MAP.put(AddressProperty.LOCALITY, + R.string.vcard_address_locality); + ADDRESS_PROPERTY_MAP.put(AddressProperty.PCODE, + R.string.vcard_address_pcode); + ADDRESS_PROPERTY_MAP.put(AddressProperty.POBOX, + R.string.vcard_address_pobox); + ADDRESS_PROPERTY_MAP.put(AddressProperty.REGION, + R.string.vcard_address_region); + ADDRESS_PROPERTY_MAP.put(AddressProperty.STREET, + R.string.vcard_address_street); + if (ADDRESS_PROPERTY_MAP.size() != AddressProperty.values().length) + throw new IllegalStateException(); + + TELEPHONE_TYPE_MAP.put(TelephoneType.BBS, R.string.bbs); + TELEPHONE_TYPE_MAP.put(TelephoneType.CELL, R.string.vcard_type_cell); + TELEPHONE_TYPE_MAP.put(TelephoneType.FAX, R.string.vcard_type_fax); + TELEPHONE_TYPE_MAP.put(TelephoneType.HOME, R.string.vcard_type_home); + TELEPHONE_TYPE_MAP.put(TelephoneType.ISDN, R.string.isdn); + TELEPHONE_TYPE_MAP.put(TelephoneType.MODEM, R.string.vcard_type_modem); + TELEPHONE_TYPE_MAP.put(TelephoneType.MSG, R.string.vcard_type_msg); + TELEPHONE_TYPE_MAP.put(TelephoneType.PAGER, R.string.vcard_type_pager); + TELEPHONE_TYPE_MAP.put(TelephoneType.PCS, R.string.pcs); + TELEPHONE_TYPE_MAP.put(TelephoneType.PREF, R.string.vcard_type_pref); + TELEPHONE_TYPE_MAP.put(TelephoneType.VIDEO, R.string.vcard_type_video); + TELEPHONE_TYPE_MAP.put(TelephoneType.VOICE, R.string.vcard_type_voice); + TELEPHONE_TYPE_MAP.put(TelephoneType.WORK, R.string.vcard_type_work); + if (TELEPHONE_TYPE_MAP.size() != TelephoneType.values().length) + throw new IllegalStateException(); + + EMAIL_TYPE_MAP.put(EmailType.HOME, R.string.vcard_type_home); + EMAIL_TYPE_MAP.put(EmailType.INTERNET, R.string.vcard_type_internet); + EMAIL_TYPE_MAP.put(EmailType.PREF, R.string.vcard_type_pref); + EMAIL_TYPE_MAP.put(EmailType.WORK, R.string.vcard_type_work); + EMAIL_TYPE_MAP.put(EmailType.X400, R.string.x400); + if (EMAIL_TYPE_MAP.size() != EmailType.values().length) + throw new IllegalStateException(); + } + + public static Map getAddressTypeMap() { + return ADDRESS_TYPE_MAP; + } + + public static Map getAddressPropertyMap() { + return ADDRESS_PROPERTY_MAP; + } + + public static Map getTelephoneTypeMap() { + return TELEPHONE_TYPE_MAP; + } + + public static Map getEmailTypeMap() { + return EMAIL_TYPE_MAP; + } +} diff --git a/app/src/main/java/com/xabber/android/data/account/AccountAuthenticator.java b/app/src/main/java/com/xabber/android/data/account/AccountAuthenticator.java index e075456f29..e81b2854e7 100644 --- a/app/src/main/java/com/xabber/android/data/account/AccountAuthenticator.java +++ b/app/src/main/java/com/xabber/android/data/account/AccountAuthenticator.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -27,77 +27,76 @@ /** * Authenticator used for contact list integration. - * + * * @author alexander.ivanov - * */ public class AccountAuthenticator extends AbstractAccountAuthenticator { - private final static AccountAuthenticator instance; + private final static AccountAuthenticator instance; - static { - instance = new AccountAuthenticator(); - Application.getInstance().addManager(instance); - } + static { + instance = new AccountAuthenticator(); + Application.getInstance().addManager(instance); + } - public static AccountAuthenticator getInstance() { - return instance; - } + public static AccountAuthenticator getInstance() { + return instance; + } - private AccountAuthenticator() { - super(Application.getInstance()); - } + private AccountAuthenticator() { + super(Application.getInstance()); + } - @Override - public Bundle addAccount(AccountAuthenticatorResponse response, - String accountType, String authTokenType, - String[] requiredFeatures, Bundle options) - throws NetworkErrorException { - Intent intent = AccountAdd.createIntent(Application.getInstance()); - intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, - response); - Bundle bundle = new Bundle(); - bundle.putParcelable(AccountManager.KEY_INTENT, intent); - return bundle; - } + @Override + public Bundle addAccount(AccountAuthenticatorResponse response, + String accountType, String authTokenType, + String[] requiredFeatures, Bundle options) + throws NetworkErrorException { + Intent intent = AccountAdd.createIntent(Application.getInstance()); + intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, + response); + Bundle bundle = new Bundle(); + bundle.putParcelable(AccountManager.KEY_INTENT, intent); + return bundle; + } - @Override - public Bundle confirmCredentials(AccountAuthenticatorResponse response, - Account account, Bundle options) { - return null; - } + @Override + public Bundle confirmCredentials(AccountAuthenticatorResponse response, + Account account, Bundle options) { + return null; + } - @Override - public Bundle editProperties(AccountAuthenticatorResponse response, - String accountType) { - throw new UnsupportedOperationException(); - } + @Override + public Bundle editProperties(AccountAuthenticatorResponse response, + String accountType) { + throw new UnsupportedOperationException(); + } - @Override - public Bundle getAuthToken(AccountAuthenticatorResponse response, - Account account, String authTokenType, Bundle loginOptions) - throws NetworkErrorException { - return null; - } + @Override + public Bundle getAuthToken(AccountAuthenticatorResponse response, + Account account, String authTokenType, Bundle loginOptions) + throws NetworkErrorException { + return null; + } - @Override - public String getAuthTokenLabel(String authTokenType) { - return null; - } + @Override + public String getAuthTokenLabel(String authTokenType) { + return null; + } - @Override - public Bundle hasFeatures(AccountAuthenticatorResponse response, - Account account, String[] features) throws NetworkErrorException { - final Bundle result = new Bundle(); - result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false); - return result; - } + @Override + public Bundle hasFeatures(AccountAuthenticatorResponse response, + Account account, String[] features) throws NetworkErrorException { + final Bundle result = new Bundle(); + result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false); + return result; + } - @Override - public Bundle updateCredentials(AccountAuthenticatorResponse response, - Account account, String authTokenType, Bundle loginOptions) - throws NetworkErrorException { - return null; - } + @Override + public Bundle updateCredentials(AccountAuthenticatorResponse response, + Account account, String authTokenType, Bundle loginOptions) + throws NetworkErrorException { + return null; + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/data/account/AccountAuthorizationError.java b/app/src/main/java/com/xabber/android/data/account/AccountAuthorizationError.java index 8176560d31..ac3865e9f8 100644 --- a/app/src/main/java/com/xabber/android/data/account/AccountAuthorizationError.java +++ b/app/src/main/java/com/xabber/android/data/account/AccountAuthorizationError.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,34 +16,34 @@ import android.content.Intent; +import com.xabber.android.R; import com.xabber.android.data.Application; import com.xabber.android.data.entity.AccountRelated; import com.xabber.android.data.notification.AccountNotificationItem; -import com.xabber.android.ui.AccountEditor; -import com.xabber.androiddev.R; +import com.xabber.android.ui.preferences.AccountEditor; public class AccountAuthorizationError extends AccountRelated implements - AccountNotificationItem { - - public AccountAuthorizationError(String account) { - super(account); - } - - @Override - public Intent getIntent() { - return AccountEditor.createIntent( - Application.getInstance(), account); - } - - @Override - public String getTitle() { - return Application.getInstance().getString( - R.string.AUTHENTICATION_FAILED); - } - - @Override - public String getText() { - return AccountManager.getInstance().getVerboseName(account); - } + AccountNotificationItem { + + public AccountAuthorizationError(String account) { + super(account); + } + + @Override + public Intent getIntent() { + return AccountEditor.createIntent( + Application.getInstance(), account); + } + + @Override + public String getTitle() { + return Application.getInstance().getString( + R.string.AUTHENTICATION_FAILED); + } + + @Override + public String getText() { + return AccountManager.getInstance().getVerboseName(account); + } } diff --git a/app/src/main/java/com/xabber/android/data/account/AccountItem.java b/app/src/main/java/com/xabber/android/data/account/AccountItem.java index 0e04129bf4..42d052c691 100644 --- a/app/src/main/java/com/xabber/android/data/account/AccountItem.java +++ b/app/src/main/java/com/xabber/android/data/account/AccountItem.java @@ -1,25 +1,20 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data.account; -import java.security.KeyPair; -import java.util.Date; - -import org.jivesoftware.smack.packet.Presence; -import org.jivesoftware.smack.packet.Presence.Type; - +import com.xabber.android.R; import com.xabber.android.data.NetworkException; import com.xabber.android.data.SettingsManager; import com.xabber.android.data.connection.ConnectionItem; @@ -27,414 +22,418 @@ import com.xabber.android.data.connection.ConnectionThread; import com.xabber.android.data.connection.ProxyType; import com.xabber.android.data.connection.TLSMode; -import com.xabber.androiddev.R; + +import org.jivesoftware.smack.packet.Presence; +import org.jivesoftware.smack.packet.Presence.Type; + +import java.security.KeyPair; +import java.util.Date; /** * Represent account settings and status. - * + * * @author alexander.ivanov - * */ public class AccountItem extends ConnectionItem { - public static final String UNDEFINED_PASSWORD = "com.xabber.android.data.core.AccountItem.UNDEFINED_PASSWORD"; - - /** - * Id in database. - * - * MUST BE USED FROM BACKGROUND THREAD ONLY! - */ - private Long id; - - /** - * Full jid calculated according to {@link #userName}, {@link #serverName}, - * {@link #resource}. - */ - private final String account; - - private final int colorIndex; - - /** - * Whether account is enabled. - */ - private boolean enabled; - - /** - * Whether roster contacts can be synchronized with system contact list. - */ - private boolean syncable; - - /** - * Whether password must be stored in database. - */ - private boolean storePassword; - - /** - * Whether authorization was failed. - */ - private boolean authFailed; - - /** - * Whether certificate is invalid. - */ - private boolean invalidCertificate; - - /** - * Whether password was requested. - */ - private boolean passwordRequested; - - private int priority; - - private StatusMode statusMode; - - private String statusText; - - /** - * OTR key pair. - */ - private KeyPair keyPair; - - /** - * Last history synchronization. - */ - private Date lastSync; - - private ArchiveMode archiveMode; - - public AccountItem(AccountProtocol protocol, boolean custom, String host, - int port, String serverName, String userName, String resource, - boolean storePassword, String password, int colorIndex, - int priority, StatusMode statusMode, String statusText, - boolean enabled, boolean saslEnabled, TLSMode tlsMode, - boolean compression, ProxyType proxyType, String proxyHost, - int proxyPort, String proxyUser, String proxyPassword, - boolean syncable, KeyPair keyPair, Date lastSync, - ArchiveMode archiveMode) { - super(protocol, custom, host, port, serverName, userName, resource, - storePassword, password, saslEnabled, tlsMode, compression, - proxyType, proxyHost, proxyPort, proxyUser, proxyPassword); - this.id = null; - this.account = userName + "@" + serverName + "/" + resource; - this.colorIndex = colorIndex; - - this.enabled = enabled; - this.priority = getValidPriority(priority); - this.statusMode = statusMode; - this.statusText = statusText; - this.syncable = syncable; - this.storePassword = storePassword; - this.keyPair = keyPair; - this.lastSync = lastSync; - this.archiveMode = archiveMode; - authFailed = false; - invalidCertificate = false; - passwordRequested = false; - } - - /** - * @return ID in database. - */ - Long getId() { - return id; - } - - /** - * Set id in db. - * - * MUST BE MANAGED FROM BACKGROUND THREAD ONLY. - * - * @param id - */ - void setId(long id) { - this.id = id; - } - - /** - * @return Account's JID. - */ - public String getAccount() { - return account; - } - - /** - * @return Assigned color. - */ - public int getColorIndex() { - return colorIndex; - } - - /** - * @return Whether roster contacts can be synchronized with system contact - * list. - */ - public boolean isSyncable() { - return syncable; - } - - /** - * Sets whether roster contacts can be synchronized with system contact - * list. - * - * @param syncable - */ - void setSyncable(boolean syncable) { - this.syncable = syncable; - } - - /** - * @return Whether password must be stored in database. - */ - public boolean isStorePassword() { - return storePassword; - } - - /** - * Sets whether password must be stored in database. - * - * @param storePassword - */ - void setStorePassword(boolean storePassword) { - this.storePassword = storePassword; - } - - public KeyPair getKeyPair() { - return keyPair; - } - - void setKeyPair(KeyPair keyPair) { - this.keyPair = keyPair; - } - - public Date getLastSync() { - return lastSync; - } - - void setLastSync(Date lastSync) { - this.lastSync = lastSync; - } - - public ArchiveMode getArchiveMode() { - return archiveMode; - } - - void setArchiveMode(ArchiveMode archiveMode) { - this.archiveMode = archiveMode; - } - - public int getPriority() { - return priority; - } - - void setPriority(int priority) { - this.priority = getValidPriority(priority); - } - - void setStatus(StatusMode statusMode, String statusText) { - this.statusMode = statusMode; - this.statusText = statusText; - } - - /** - * @return Saved status mode. - */ - StatusMode getRawStatusMode() { - return statusMode; - } - - /** - * @return Status text. - */ - public String getStatusText() { - return statusText; - } - - /** - * @return Actual status mode, {@link StatusMode#connection} or - * {@link StatusMode#unavailable} to display. - */ - public StatusMode getDisplayStatusMode() { - ConnectionState state = getState(); - if (state.isConnected()) - return statusMode; - else if (state.isConnectable()) - return StatusMode.connection; - else - return StatusMode.unavailable; - } - - /** - * @return Status mode or {@link StatusMode#unavailable} if account was not - * authenticated to be used in status editor. - */ - public StatusMode getFactualStatusMode() { - if (getState().isConnected()) - return statusMode; - else - return StatusMode.unavailable; - } - - /** - * @return {@link Presence} to be send. - * @throws NetworkException - */ - public Presence getPresence() throws NetworkException { - StatusMode statusMode = getFactualStatusMode(); - if (statusMode == StatusMode.unsubscribed) - throw new IllegalStateException(); - if (statusMode == StatusMode.unavailable) - throw new NetworkException(R.string.NOT_CONNECTED); - if (statusMode == StatusMode.invisible) - return new Presence(Type.unavailable); - else { - int priority; - if (statusMode != StatusMode.dnd) { - if (AccountManager.getInstance().isXa()) - statusMode = StatusMode.xa; - else if (AccountManager.getInstance().isAway()) - statusMode = StatusMode.away; - } - if (SettingsManager.connectionAdjustPriority()) { - if (statusMode == StatusMode.available) - priority = SettingsManager.connectionPriorityAvailable(); - else if (statusMode == StatusMode.away) - priority = SettingsManager.connectionPriorityAway(); - else if (statusMode == StatusMode.chat) - priority = SettingsManager.connectionPriorityChat(); - else if (statusMode == StatusMode.dnd) - priority = SettingsManager.connectionPriorityDnd(); - else if (statusMode == StatusMode.xa) - priority = SettingsManager.connectionPriorityXa(); - else - throw new IllegalStateException(); - } else - priority = this.priority; - return new Presence(Type.available, statusText, priority, - statusMode.getMode()); - } - } - - /** - * @return Whether account is enabled. - */ - public boolean isEnabled() { - return enabled; - } - - void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - /** - * Update connection options - * - * @param custom - * @param host - * @param port - * @param password - * @param saslEnabled - * @param tlsMode - * @param compression - */ - void updateConnectionSettings(boolean custom, String host, int port, - String password, boolean saslEnabled, TLSMode tlsMode, - boolean compression, ProxyType proxyType, String proxyHost, - int proxyPort, String proxyUser, String proxyPassword) { - getConnectionSettings().update(custom, host, port, password, - saslEnabled, tlsMode, compression, proxyType, proxyHost, - proxyPort, proxyUser, proxyPassword); - passwordRequested = false; - AccountManager.getInstance().removePasswordRequest(account); - } - - @Override - protected boolean isConnectionAvailable(boolean userRequest) { - // Check password before go online. - if (statusMode.isOnline() - && enabled - && !passwordRequested - && UNDEFINED_PASSWORD.equals(getConnectionSettings() - .getPassword())) { - passwordRequested = true; - AccountManager.getInstance().addPasswordRequest(account); - } - if (userRequest) { - authFailed = false; - invalidCertificate = false; - } - return statusMode.isOnline() && enabled && !authFailed - && !invalidCertificate && !passwordRequested; - } - - /** - * Remove password and update notification if {@link #storePassword} is - * disabled. - */ - void clearPassword() { - if (storePassword) - return; - passwordRequested = false; - AccountManager.getInstance().removePasswordRequest(account); - getConnectionSettings().setPassword(UNDEFINED_PASSWORD); - } - - @Override - protected void onPasswordChanged(String password) { - super.onPasswordChanged(password); - AccountManager.getInstance().requestToWriteAccount(this); - } - - @Override - protected void onSRVResolved(ConnectionThread connectionThread) { - super.onSRVResolved(connectionThread); - AccountManager.getInstance().onAccountChanged(account); - } - - @Override - protected void onInvalidCertificate() { - super.onInvalidCertificate(); - invalidCertificate = true; - updateConnection(false); - } - - @Override - protected void onConnected(ConnectionThread connectionThread) { - super.onConnected(connectionThread); - AccountManager.getInstance().onAccountChanged(account); - } - - @Override - protected void onAuthFailed() { - super.onAuthFailed(); - // Login failed. We don`t want to reconnect. - authFailed = true; - updateConnection(false); - AccountManager.getInstance().addAuthenticationError(account); - } - - @Override - protected void onAuthorized(ConnectionThread connectionThread) { - super.onAuthorized(connectionThread); - AccountManager.getInstance().onAccountChanged(account); - } - - @Override - protected void onClose(ConnectionThread connectionThread) { - super.onClose(connectionThread); - AccountManager.getInstance().onAccountChanged(account); - } - - @Override - public String toString() { - return super.toString() + ":" + getAccount(); - } - - /** - * @param priority - * @return Valid priority value between -128 and 128. - */ - static private int getValidPriority(int priority) { - return Math.min(128, Math.max(-128, priority)); - } - + public static final String UNDEFINED_PASSWORD = "com.xabber.android.data.core.AccountItem.UNDEFINED_PASSWORD"; + /** + * Full jid calculated according to {@link #userName}, {@link #serverName}, + * {@link #resource}. + */ + private final String account; + /** + * Id in database. + *

+ * MUST BE USED FROM BACKGROUND THREAD ONLY! + */ + private Long id; + private int colorIndex; + + /** + * Whether account is enabled. + */ + private boolean enabled; + + /** + * Whether roster contacts can be synchronized with system contact list. + */ + private boolean syncable; + + /** + * Whether password must be stored in database. + */ + private boolean storePassword; + + /** + * Whether authorization was failed. + */ + private boolean authFailed; + + /** + * Whether certificate is invalid. + */ + private boolean invalidCertificate; + + /** + * Whether password was requested. + */ + private boolean passwordRequested; + + private int priority; + + private StatusMode statusMode; + + private String statusText; + + /** + * OTR key pair. + */ + private KeyPair keyPair; + + /** + * Last history synchronization. + */ + private Date lastSync; + + private ArchiveMode archiveMode; + + public AccountItem(AccountProtocol protocol, boolean custom, String host, + int port, String serverName, String userName, String resource, + boolean storePassword, String password, int colorIndex, + int priority, StatusMode statusMode, String statusText, + boolean enabled, boolean saslEnabled, TLSMode tlsMode, + boolean compression, ProxyType proxyType, String proxyHost, + int proxyPort, String proxyUser, String proxyPassword, + boolean syncable, KeyPair keyPair, Date lastSync, + ArchiveMode archiveMode) { + super(protocol, custom, host, port, serverName, userName, resource, + storePassword, password, saslEnabled, tlsMode, compression, + proxyType, proxyHost, proxyPort, proxyUser, proxyPassword); + this.id = null; + this.account = userName + "@" + serverName + "/" + resource; + this.colorIndex = colorIndex; + + this.enabled = enabled; + this.priority = getValidPriority(priority); + this.statusMode = statusMode; + this.statusText = statusText; + this.syncable = syncable; + this.storePassword = storePassword; + this.keyPair = keyPair; + this.lastSync = lastSync; + this.archiveMode = archiveMode; + authFailed = false; + invalidCertificate = false; + passwordRequested = false; + } + + /** + * @param priority + * @return Valid priority value between -128 and 128. + */ + static private int getValidPriority(int priority) { + return Math.min(128, Math.max(-128, priority)); + } + + /** + * @return ID in database. + */ + Long getId() { + return id; + } + + /** + * Set id in db. + *

+ * MUST BE MANAGED FROM BACKGROUND THREAD ONLY. + * + * @param id + */ + void setId(long id) { + this.id = id; + } + + /** + * @return Account's JID. + */ + public String getAccount() { + return account; + } + + /** + * @return Assigned color. + */ + public int getColorIndex() { + return colorIndex; + } + + public void setColorIndex(int colorIndex) { + this.colorIndex = colorIndex; + } + + /** + * @return Whether roster contacts can be synchronized with system contact + * list. + */ + public boolean isSyncable() { + return syncable; + } + + /** + * Sets whether roster contacts can be synchronized with system contact + * list. + * + * @param syncable + */ + void setSyncable(boolean syncable) { + this.syncable = syncable; + } + + /** + * @return Whether password must be stored in database. + */ + public boolean isStorePassword() { + return storePassword; + } + + /** + * Sets whether password must be stored in database. + * + * @param storePassword + */ + void setStorePassword(boolean storePassword) { + this.storePassword = storePassword; + } + + public KeyPair getKeyPair() { + return keyPair; + } + + void setKeyPair(KeyPair keyPair) { + this.keyPair = keyPair; + } + + public Date getLastSync() { + return lastSync; + } + + void setLastSync(Date lastSync) { + this.lastSync = lastSync; + } + + public ArchiveMode getArchiveMode() { + return archiveMode; + } + + void setArchiveMode(ArchiveMode archiveMode) { + this.archiveMode = archiveMode; + } + + public int getPriority() { + return priority; + } + + void setPriority(int priority) { + this.priority = getValidPriority(priority); + } + + void setStatus(StatusMode statusMode, String statusText) { + this.statusMode = statusMode; + this.statusText = statusText; + } + + /** + * @return Saved status mode. + */ + StatusMode getRawStatusMode() { + return statusMode; + } + + /** + * @return Status text. + */ + public String getStatusText() { + return statusText; + } + + /** + * @return Actual status mode, {@link StatusMode#connection} or + * {@link StatusMode#unavailable} to display. + */ + public StatusMode getDisplayStatusMode() { + ConnectionState state = getState(); + if (state.isConnected()) + return statusMode; + else if (state.isConnectable()) + return StatusMode.connection; + else + return StatusMode.unavailable; + } + + /** + * @return Status mode or {@link StatusMode#unavailable} if account was not + * authenticated to be used in status editor. + */ + public StatusMode getFactualStatusMode() { + if (getState().isConnected()) + return statusMode; + else + return StatusMode.unavailable; + } + + /** + * @return {@link Presence} to be send. + * @throws NetworkException + */ + public Presence getPresence() throws NetworkException { + StatusMode statusMode = getFactualStatusMode(); + if (statusMode == StatusMode.unsubscribed) + throw new IllegalStateException(); + if (statusMode == StatusMode.unavailable) + throw new NetworkException(R.string.NOT_CONNECTED); + if (statusMode == StatusMode.invisible) + return new Presence(Type.unavailable); + else { + int priority; + if (statusMode != StatusMode.dnd) { + if (AccountManager.getInstance().isXa()) + statusMode = StatusMode.xa; + else if (AccountManager.getInstance().isAway()) + statusMode = StatusMode.away; + } + if (SettingsManager.connectionAdjustPriority()) { + if (statusMode == StatusMode.available) + priority = SettingsManager.connectionPriorityAvailable(); + else if (statusMode == StatusMode.away) + priority = SettingsManager.connectionPriorityAway(); + else if (statusMode == StatusMode.chat) + priority = SettingsManager.connectionPriorityChat(); + else if (statusMode == StatusMode.dnd) + priority = SettingsManager.connectionPriorityDnd(); + else if (statusMode == StatusMode.xa) + priority = SettingsManager.connectionPriorityXa(); + else + throw new IllegalStateException(); + } else + priority = this.priority; + return new Presence(Type.available, statusText, priority, + statusMode.getMode()); + } + } + + /** + * @return Whether account is enabled. + */ + public boolean isEnabled() { + return enabled; + } + + void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + /** + * Update connection options + * + * @param custom + * @param host + * @param port + * @param password + * @param saslEnabled + * @param tlsMode + * @param compression + */ + void updateConnectionSettings(boolean custom, String host, int port, + String password, boolean saslEnabled, TLSMode tlsMode, + boolean compression, ProxyType proxyType, String proxyHost, + int proxyPort, String proxyUser, String proxyPassword) { + getConnectionSettings().update(custom, host, port, password, + saslEnabled, tlsMode, compression, proxyType, proxyHost, + proxyPort, proxyUser, proxyPassword); + passwordRequested = false; + AccountManager.getInstance().removePasswordRequest(account); + } + + @Override + protected boolean isConnectionAvailable(boolean userRequest) { + // Check password before go online. + if (statusMode.isOnline() + && enabled + && !passwordRequested + && UNDEFINED_PASSWORD.equals(getConnectionSettings() + .getPassword())) { + passwordRequested = true; + AccountManager.getInstance().addPasswordRequest(account); + } + if (userRequest) { + authFailed = false; + invalidCertificate = false; + } + return statusMode.isOnline() && enabled && !authFailed + && !invalidCertificate && !passwordRequested; + } + + /** + * Remove password and update notification if {@link #storePassword} is + * disabled. + */ + void clearPassword() { + if (storePassword) + return; + passwordRequested = false; + AccountManager.getInstance().removePasswordRequest(account); + getConnectionSettings().setPassword(UNDEFINED_PASSWORD); + } + + @Override + protected void onPasswordChanged(String password) { + super.onPasswordChanged(password); + AccountManager.getInstance().requestToWriteAccount(this); + } + + @Override + protected void onSRVResolved(ConnectionThread connectionThread) { + super.onSRVResolved(connectionThread); + AccountManager.getInstance().onAccountChanged(account); + } + + @Override + protected void onInvalidCertificate() { + super.onInvalidCertificate(); + invalidCertificate = true; + updateConnection(false); + } + + @Override + protected void onConnected(ConnectionThread connectionThread) { + super.onConnected(connectionThread); + AccountManager.getInstance().onAccountChanged(account); + } + + @Override + protected void onAuthFailed() { + super.onAuthFailed(); + // Login failed. We don`t want to reconnect. + authFailed = true; + updateConnection(false); + AccountManager.getInstance().addAuthenticationError(account); + } + + @Override + protected void onAuthorized(ConnectionThread connectionThread) { + super.onAuthorized(connectionThread); + AccountManager.getInstance().onAccountChanged(account); + } + + @Override + protected void onClose(ConnectionThread connectionThread) { + super.onClose(connectionThread); + AccountManager.getInstance().onAccountChanged(account); + } + + @Override + public String toString() { + return super.toString() + ":" + getAccount(); + } } diff --git a/app/src/main/java/com/xabber/android/data/account/AccountManager.java b/app/src/main/java/com/xabber/android/data/account/AccountManager.java index 6c4a1eb6b9..dab4e781b7 100644 --- a/app/src/main/java/com/xabber/android/data/account/AccountManager.java +++ b/app/src/main/java/com/xabber/android/data/account/AccountManager.java @@ -1,39 +1,30 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data.account; -import java.security.KeyPair; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; - -import org.jivesoftware.smack.util.StringUtils; - import android.content.res.TypedArray; import android.database.Cursor; +import android.os.Build; +import com.xabber.android.R; import com.xabber.android.data.Application; import com.xabber.android.data.NetworkException; import com.xabber.android.data.OnLoadListener; import com.xabber.android.data.OnWipeListener; import com.xabber.android.data.SettingsManager; +import com.xabber.android.data.connection.ConnectionSettings; import com.xabber.android.data.connection.ConnectionState; import com.xabber.android.data.connection.ProxyType; import com.xabber.android.data.connection.TLSMode; @@ -42,1093 +33,1103 @@ import com.xabber.android.data.notification.NotificationManager; import com.xabber.android.data.roster.PresenceManager; import com.xabber.android.data.roster.RosterManager; -import com.xabber.androiddev.R; import com.xabber.xmpp.address.Jid; +import org.jivesoftware.smack.util.StringUtils; + +import java.security.KeyPair; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + /** * This class manage all operations with accounts. - * - *

+ *

+ *

* Each account has unique full jid (userName@serverName/resource). This jid is * persistent and independent from real jid assigned by server. Real full jid * (assigned by server) of account can be taken by * {@link AccountItem#getRealAccount()}. - * + * * @author alexander.ivanov - * */ public class AccountManager implements OnLoadListener, OnWipeListener { - /** - * List of account presets. - */ - private final List accountTypes; - - /** - * List of saved statuses. - */ - private final Collection savedStatuses; - - /** - * Number of different account colors. - */ - private final int colors; - - /** - * List of accounts. - */ - private final Map accountItems; - - /** - * List of enabled account. - */ - private final Collection enabledAccounts; - - /** - * Whether away status mode is enabled. - */ - private boolean away; - - /** - * Whether extended away mode is enabled. - */ - private boolean xa; - - private final BaseAccountNotificationProvider authorizationErrorProvider; - - private final BaseAccountNotificationProvider passwordRequestProvider; - - private final Application application; - - private final static AccountManager instance; - - static { - instance = new AccountManager(); - Application.getInstance().addManager(instance); - } - - public static AccountManager getInstance() { - return instance; - } - - private AccountManager() { - this.application = Application.getInstance(); - accountItems = new HashMap(); - enabledAccounts = new HashSet(); - savedStatuses = new ArrayList(); - authorizationErrorProvider = new BaseAccountNotificationProvider( - R.drawable.ic_stat_auth_failed); - passwordRequestProvider = new BaseAccountNotificationProvider( - R.drawable.ic_stat_request); - - TypedArray accountAvatars = application.getResources() - .obtainTypedArray(R.array.account_avatars); - colors = accountAvatars.length(); - accountAvatars.recycle(); - - TypedArray types = application.getResources().obtainTypedArray( - R.array.account_types); - accountTypes = new ArrayList(); - for (int index = 0; index < types.length(); index++) { - int id = types.getResourceId(index, 0); - TypedArray values = application.getResources().obtainTypedArray(id); - AccountProtocol protocol = AccountProtocol.valueOf(values - .getString(0)); - if (Application.SDK_INT < 8 && protocol == AccountProtocol.wlm) { - values.recycle(); - continue; - } - ArrayList servers = new ArrayList(); - servers.add(values.getString(9)); - for (int i = 10; i < values.length(); i++) - servers.add(values.getString(i)); - accountTypes.add(new AccountType(id, protocol, values.getString(1), - values.getString(2), values.getString(3), values - .getDrawable(4), values.getBoolean(5, false), - values.getString(6), values.getInt(7, 5222), values - .getBoolean(8, false), servers)); - values.recycle(); - } - types.recycle(); - away = false; - xa = false; - } - - @Override - public void onLoad() { - final Collection savedStatuses = new ArrayList(); - final Collection accountItems = new ArrayList(); - Cursor cursor = StatusTable.getInstance().list(); - try { - if (cursor.moveToFirst()) { - do { - savedStatuses.add(new SavedStatus(StatusTable - .getStatusMode(cursor), StatusTable - .getStatusText(cursor))); - } while (cursor.moveToNext()); - } - } finally { - cursor.close(); - } - - cursor = AccountTable.getInstance().list(); - try { - if (cursor.moveToFirst()) { - do { - AccountItem accountItem = new AccountItem( - AccountTable.getProtocol(cursor), - AccountTable.isCustom(cursor), - AccountTable.getHost(cursor), - AccountTable.getPort(cursor), - AccountTable.getServerName(cursor), - AccountTable.getUserName(cursor), - AccountTable.getResource(cursor), - AccountTable.isStorePassword(cursor), - AccountTable.getPassword(cursor), - AccountTable.getColorIndex(cursor), - AccountTable.getPriority(cursor), - AccountTable.getStatusMode(cursor), - AccountTable.getStatusText(cursor), - AccountTable.isEnabled(cursor), - AccountTable.isSaslEnabled(cursor), - AccountTable.getTLSMode(cursor), - AccountTable.isCompression(cursor), - AccountTable.getProxyType(cursor), - AccountTable.getProxyHost(cursor), - AccountTable.getProxyPort(cursor), - AccountTable.getProxyUser(cursor), - AccountTable.getProxyPassword(cursor), - AccountTable.isSyncable(cursor), - AccountTable.getKeyPair(cursor), - AccountTable.getLastSync(cursor), - AccountTable.getArchiveMode(cursor)); - accountItem.setId(AccountTable.getId(cursor)); - accountItems.add(accountItem); - } while (cursor.moveToNext()); - } - } finally { - cursor.close(); - } - - Application.getInstance().runOnUiThread(new Runnable() { - @Override - public void run() { - onLoaded(savedStatuses, accountItems); - } - }); - } - - private void onLoaded(Collection savedStatuses, - Collection accountItems) { - this.savedStatuses.addAll(savedStatuses); - for (AccountItem accountItem : accountItems) - addAccount(accountItem); - NotificationManager.getInstance().registerNotificationProvider( - authorizationErrorProvider); - NotificationManager.getInstance().registerNotificationProvider( - passwordRequestProvider); - } - - private void addAccount(AccountItem accountItem) { - accountItems.put(accountItem.getAccount(), accountItem); - if (accountItem.isEnabled()) - enabledAccounts.add(accountItem.getAccount()); - for (OnAccountAddedListener listener : application - .getManagers(OnAccountAddedListener.class)) - listener.onAccountAdded(accountItem); - if (accountItem.isEnabled()) { - onAccountEnabled(accountItem); - if (accountItem.getRawStatusMode().isOnline()) - onAccountOnline(accountItem); - } - onAccountChanged(accountItem.getAccount()); - } - - /** - * @return List of supported account types. - */ - public List getAccountTypes() { - return accountTypes; - } - - /** - * @return Next color index for the next account. - */ - int getNextColorIndex() { - int[] count = new int[colors]; - for (AccountItem accountItem : accountItems.values()) - count[accountItem.getColorIndex() % colors] += 1; - int result = 0; - int value = count[0]; - for (int index = 0; index < count.length; index++) - if (count[index] < value) - result = index; - return result; - } - - /** - * @param account - * full jid. - * @return Specified account or null if account doesn't exists. - */ - public AccountItem getAccount(String account) { - return accountItems.get(account); - } - - /** - * Save account item to database. - * - * @param accountItem - */ - void requestToWriteAccount(final AccountItem accountItem) { - final AccountProtocol protocol = accountItem.getConnectionSettings() - .getProtocol(); - final boolean custom = accountItem.getConnectionSettings().isCustom(); - final String host = accountItem.getConnectionSettings().getHost(); - final int port = accountItem.getConnectionSettings().getPort(); - final String serverName = accountItem.getConnectionSettings() - .getServerName(); - final String userName = accountItem.getConnectionSettings() - .getUserName(); - final String resource = accountItem.getConnectionSettings() - .getResource(); - final boolean storePassword = accountItem.isStorePassword(); - final String password = accountItem.getConnectionSettings() - .getPassword(); - final int colorIndex = accountItem.getColorIndex(); - final int priority = accountItem.getPriority(); - final StatusMode statusMode = accountItem.getRawStatusMode(); - final String statusText = accountItem.getStatusText(); - final boolean enabled = accountItem.isEnabled(); - final boolean saslEnabled = accountItem.getConnectionSettings() - .isSaslEnabled(); - final TLSMode tlsMode = accountItem.getConnectionSettings() - .getTlsMode(); - final boolean compression = accountItem.getConnectionSettings() - .useCompression(); - final ProxyType proxyType = accountItem.getConnectionSettings() - .getProxyType(); - final String proxyHost = accountItem.getConnectionSettings() - .getProxyHost(); - final int proxyPort = accountItem.getConnectionSettings() - .getProxyPort(); - final String proxyUser = accountItem.getConnectionSettings() - .getProxyUser(); - final String proxyPassword = accountItem.getConnectionSettings() - .getProxyPassword(); - final boolean syncable = accountItem.isSyncable(); - final KeyPair keyPair = accountItem.getKeyPair(); - final Date lastSync = accountItem.getLastSync(); - final ArchiveMode archiveMode = accountItem.getArchiveMode(); - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - accountItem.setId(AccountTable.getInstance().write( - accountItem.getId(), protocol, custom, host, port, - serverName, userName, resource, storePassword, - password, colorIndex, priority, statusMode, statusText, - enabled, saslEnabled, tlsMode, compression, proxyType, - proxyHost, proxyPort, proxyUser, proxyPassword, - syncable, keyPair, lastSync, archiveMode)); - } - }); - } - - /** - * Creates new account and starts connection. - */ - private AccountItem addAccount(AccountProtocol protocol, boolean custom, - String host, int port, String serverName, String userName, - boolean storePassword, String password, String resource, int color, - int priority, StatusMode statusMode, String statusText, - boolean enabled, boolean saslEnabled, TLSMode tlsMode, - boolean compression, ProxyType proxyType, String proxyHost, - int proxyPort, String proxyUser, String proxyPassword, - boolean syncable, KeyPair keyPair, Date lastSync, - ArchiveMode archiveMode) { - AccountItem accountItem = new AccountItem(protocol, custom, host, port, - serverName, userName, resource, storePassword, password, color, - priority, statusMode, statusText, enabled, saslEnabled, - tlsMode, compression, proxyType, proxyHost, proxyPort, - proxyUser, proxyPassword, syncable, keyPair, lastSync, - archiveMode); - requestToWriteAccount(accountItem); - addAccount(accountItem); - accountItem.updateConnection(true); - return accountItem; - } - - /** - * Creates new account. - * - * @param user - * full or bare jid. - * @param password - * @param accountType - * xmpp account type can be replaced depend on server part. - * @param syncable - * @param storePassword - * @param useOrbot - * @return assigned account name. - * @throws NetworkException - * if user or server part are invalid. - */ - public String addAccount(String user, String password, - AccountType accountType, boolean syncable, boolean storePassword, - boolean useOrbot) throws NetworkException { - if (accountType.getProtocol().isOAuth()) { - int index = 1; - while (true) { - user = String.valueOf(index); - boolean found = false; - for (AccountItem accountItem : accountItems.values()) - if (accountItem.getConnectionSettings().getServerName() - .equals(accountType.getFirstServer()) - && accountItem.getConnectionSettings() - .getUserName().equals(user)) { - found = true; - break; - } - if (!found) - break; - index++; - } - } - - if (user == null) - throw new NetworkException(R.string.EMPTY_USER_NAME); - - if (user.indexOf("@") == -1) { - if ("".equals(accountType.getFirstServer())) - throw new NetworkException(R.string.EMPTY_SERVER_NAME); - else - user += "@" + accountType.getFirstServer(); - } - - String serverName = StringUtils.parseServer(user); - String userName = StringUtils.parseName(user); - String resource = StringUtils.parseResource(user); - String host = accountType.getHost(); - int port = accountType.getPort(); - boolean tlsRequired = accountType.isTLSRequired(); - if (useOrbot) - tlsRequired = true; - - if ("".equals(serverName)) { - throw new NetworkException(R.string.EMPTY_SERVER_NAME); - } else if (!accountType.isAllowServer() - && !serverName.equals(accountType.getFirstServer())) - throw new NetworkException(R.string.INCORRECT_USER_NAME); - - if ("".equals(userName)) - throw new NetworkException(R.string.EMPTY_USER_NAME); - if ("".equals(resource)) - resource = "android" + StringUtils.randomString(8); - - if (accountType.getId() == R.array.account_type_xmpp) { - host = serverName; - for (AccountType check : accountTypes) - if (check.getServers().contains(serverName)) { - accountType = check; - host = check.getHost(); - port = check.getPort(); - tlsRequired = check.isTLSRequired(); - break; - } - } - - AccountItem accountItem; - for (;;) { - if (getAccount(userName + '@' + serverName + '/' + resource) == null) - break; - resource = "android" + StringUtils.randomString(8); - } - - accountItem = addAccount(accountType.getProtocol(), true, host, port, - serverName, userName, storePassword, password, resource, - getNextColorIndex(), 0, StatusMode.available, - SettingsManager.statusText(), true, true, - tlsRequired ? TLSMode.required : TLSMode.enabled, false, - useOrbot ? ProxyType.orbot : ProxyType.none, "localhost", 8080, - "", "", syncable, null, null, ArchiveMode.available); - onAccountChanged(accountItem.getAccount()); - if (accountItems.size() > 1 - && SettingsManager.contactsEnableShowAccounts()) - SettingsManager.enableContactsShowAccount(); - return accountItem.getAccount(); - } - - /** - * Remove user`s account. Don't call any callbacks. - * - * @param account - */ - private void removeAccountWithoutCallback(final String account) { - final AccountItem accountItem = getAccount(account); - boolean wasEnabled = accountItem.isEnabled(); - accountItem.setEnabled(false); - accountItem.updateConnection(true); - if (wasEnabled) { - if (accountItem.getRawStatusMode().isOnline()) - onAccountOffline(accountItem); - onAccountDisabled(accountItem); - } - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - AccountTable.getInstance().remove(account, accountItem.getId()); - } - }); - accountItems.remove(account); - enabledAccounts.remove(account); - for (OnAccountRemovedListener listener : application - .getManagers(OnAccountRemovedListener.class)) - listener.onAccountRemoved(accountItem); - removeAuthorizationError(account); - } - - /** - * Remove user`s account. - * - * @param account - */ - public void removeAccount(String account) { - removeAccountWithoutCallback(account); - onAccountChanged(account); - } - - /** - * Update user`s account. - * - * It will reconnect to the server if changes was made. - * - * It will remove old account and create new one if full jid was changed. - * - * @param account - * full source jid - * @param host - * @param port - * @param serverName - * @param userName - * @param storePassword - * @param password - * @param resource - * @param priority - * @param enabled - * @param saslEnabled - * @param tlsMode - * @param compression - * @param syncable - * @param archiveMode - */ - public void updateAccount(String account, boolean custom, String host, - int port, String serverName, String userName, - boolean storePassword, String password, String resource, - int priority, boolean enabled, boolean saslEnabled, - TLSMode tlsMode, boolean compression, ProxyType proxyType, - String proxyHost, int proxyPort, String proxyUser, - String proxyPassword, boolean syncable, ArchiveMode archiveMode) { - AccountItem result; - AccountItem accountItem = getAccount(account); - if (accountItem.getConnectionSettings().getServerName() - .equals(serverName) - && accountItem.getConnectionSettings().getUserName() - .equals(userName) - && accountItem.getConnectionSettings().getResource() - .equals(resource)) { - result = accountItem; - boolean reconnect = false; - if (accountItem.getConnectionSettings().isCustom() != custom - || !accountItem.getConnectionSettings().getHost() - .equals(host) - || accountItem.getConnectionSettings().getPort() != port - || !accountItem.getConnectionSettings().getPassword() - .equals(password) - || accountItem.getConnectionSettings().getTlsMode() != tlsMode - || accountItem.getConnectionSettings().isSaslEnabled() != saslEnabled - || accountItem.getConnectionSettings().useCompression() != compression - || accountItem.getConnectionSettings().getProxyType() != proxyType - || !accountItem.getConnectionSettings().getProxyHost() - .equals(proxyHost) - || accountItem.getConnectionSettings().getProxyPort() != proxyPort - || !accountItem.getConnectionSettings().getProxyUser() - .equals(proxyUser) - || !accountItem.getConnectionSettings().getProxyPassword() - .equals(proxyPassword)) { - result.updateConnectionSettings(custom, host, port, password, - saslEnabled, tlsMode, compression, proxyType, - proxyHost, proxyPort, proxyUser, proxyPassword); - reconnect = true; - } - if (result.isSyncable() != syncable) { - result.setSyncable(syncable); - for (OnAccountSyncableChangedListener listener : application - .getManagers(OnAccountSyncableChangedListener.class)) - listener.onAccountSyncableChanged(result); - } - result.setStorePassword(storePassword); - boolean changed = result.isEnabled() != enabled; - result.setEnabled(enabled); - if (result.getPriority() != priority) { - result.setPriority(priority); - try { - PresenceManager.getInstance().resendPresence(account); - } catch (NetworkException e) { - } - } - if (result.getArchiveMode() != archiveMode) { - reconnect = (result.getArchiveMode() == ArchiveMode.server) != (archiveMode == ArchiveMode.server); - result.setArchiveMode(archiveMode); - for (OnAccountArchiveModeChangedListener listener : application - .getManagers(OnAccountArchiveModeChangedListener.class)) - listener.onAccountArchiveModeChanged(result); - } - if (changed && enabled) { - enabledAccounts.add(account); - onAccountEnabled(result); - if (result.getRawStatusMode().isOnline()) - onAccountOnline(result); - } - if (changed || reconnect) { - result.updateConnection(true); - result.forceReconnect(); - } - if (changed && !enabled) { - enabledAccounts.remove(account); - if (result.getRawStatusMode().isOnline()) - onAccountOffline(result); - onAccountDisabled(result); - } - requestToWriteAccount(result); - } else { - int colorIndex = accountItem.getColorIndex(); - StatusMode statusMode = accountItem.getRawStatusMode(); - String statusText = accountItem.getStatusText(); - AccountProtocol protocol = accountItem.getConnectionSettings() - .getProtocol(); - KeyPair keyPair = accountItem.getKeyPair(); - Date lastSync = accountItem.getLastSync(); - removeAccountWithoutCallback(account); - result = addAccount(protocol, custom, host, port, serverName, - userName, storePassword, password, resource, colorIndex, - priority, statusMode, statusText, enabled, saslEnabled, - tlsMode, compression, proxyType, proxyHost, proxyPort, - proxyUser, proxyPassword, syncable, keyPair, lastSync, - archiveMode); - } - onAccountChanged(result.getAccount()); - } - - public void setKeyPair(String account, KeyPair keyPair) { - AccountItem accountItem = getAccount(account); - accountItem.setKeyPair(keyPair); - requestToWriteAccount(accountItem); - } - - public void setLastSync(String account, Date lastSync) { - AccountItem accountItem = getAccount(account); - accountItem.setLastSync(lastSync); - requestToWriteAccount(accountItem); - } - - public void setSyncable(String account, boolean syncable) { - AccountItem accountItem = getAccount(account); - updateAccount(account, accountItem.getConnectionSettings().isCustom(), - accountItem.getConnectionSettings().getHost(), accountItem - .getConnectionSettings().getPort(), accountItem - .getConnectionSettings().getServerName(), accountItem - .getConnectionSettings().getUserName(), - accountItem.isStorePassword(), accountItem - .getConnectionSettings().getPassword(), accountItem - .getConnectionSettings().getResource(), - accountItem.getPriority(), accountItem.isEnabled(), accountItem - .getConnectionSettings().isSaslEnabled(), accountItem - .getConnectionSettings().getTlsMode(), accountItem - .getConnectionSettings().useCompression(), accountItem - .getConnectionSettings().getProxyType(), accountItem - .getConnectionSettings().getProxyHost(), accountItem - .getConnectionSettings().getProxyPort(), accountItem - .getConnectionSettings().getProxyUser(), accountItem - .getConnectionSettings().getProxyPassword(), syncable, - accountItem.getArchiveMode()); - } - - public void setPassword(String account, boolean storePassword, - String password) { - AccountItem accountItem = getAccount(account); - updateAccount(account, accountItem.getConnectionSettings().isCustom(), - accountItem.getConnectionSettings().getHost(), accountItem - .getConnectionSettings().getPort(), accountItem - .getConnectionSettings().getServerName(), accountItem - .getConnectionSettings().getUserName(), storePassword, - password, accountItem.getConnectionSettings().getResource(), - accountItem.getPriority(), accountItem.isEnabled(), accountItem - .getConnectionSettings().isSaslEnabled(), accountItem - .getConnectionSettings().getTlsMode(), accountItem - .getConnectionSettings().useCompression(), accountItem - .getConnectionSettings().getProxyType(), accountItem - .getConnectionSettings().getProxyHost(), accountItem - .getConnectionSettings().getProxyPort(), accountItem - .getConnectionSettings().getProxyUser(), accountItem - .getConnectionSettings().getProxyPassword(), - accountItem.isSyncable(), accountItem.getArchiveMode()); - } - - public void setArchiveMode(String account, ArchiveMode archiveMode) { - AccountItem accountItem = AccountManager.getInstance().getAccount( - account); - AccountManager.getInstance().updateAccount(account, - accountItem.getConnectionSettings().isCustom(), - accountItem.getConnectionSettings().getHost(), - accountItem.getConnectionSettings().getPort(), - accountItem.getConnectionSettings().getServerName(), - accountItem.getConnectionSettings().getUserName(), - accountItem.isStorePassword(), - accountItem.getConnectionSettings().getPassword(), - accountItem.getConnectionSettings().getResource(), - accountItem.getPriority(), accountItem.isEnabled(), - accountItem.getConnectionSettings().isSaslEnabled(), - accountItem.getConnectionSettings().getTlsMode(), - accountItem.getConnectionSettings().useCompression(), - accountItem.getConnectionSettings().getProxyType(), - accountItem.getConnectionSettings().getProxyHost(), - accountItem.getConnectionSettings().getProxyPort(), - accountItem.getConnectionSettings().getProxyUser(), - accountItem.getConnectionSettings().getProxyPassword(), - accountItem.isSyncable(), archiveMode); - } - - public ArchiveMode getArchiveMode(String account) { - AccountItem accountItem = getAccount(account); - if (accountItem == null) - return ArchiveMode.available; - return accountItem.getArchiveMode(); - } - - /** - * @return List of enabled accounts. - */ - public Collection getAccounts() { - return Collections.unmodifiableCollection(enabledAccounts); - } - - /** - * @return List of all accounts including disabled. - */ - public Collection getAllAccounts() { - return Collections.unmodifiableCollection(accountItems.keySet()); - } - - public CommonState getCommonState() { - boolean disabled = false; - boolean offline = false; - boolean waiting = false; - boolean connecting = false; - boolean roster = false; - boolean online = false; - - for (AccountItem accountItem : accountItems.values()) { - ConnectionState state = accountItem.getState(); - if (state == ConnectionState.connected) - online = true; - if (RosterManager.getInstance().isRosterReceived( - accountItem.getAccount())) - roster = true; - if (state == ConnectionState.connecting - || state == ConnectionState.authentication) - connecting = true; - if (state == ConnectionState.waiting) - waiting = true; - if (accountItem.isEnabled()) - offline = true; - disabled = true; - } - if (online) - return CommonState.online; - else if (roster) - return CommonState.roster; - else if (connecting) - return CommonState.connecting; - if (waiting) - return CommonState.waiting; - else if (offline) - return CommonState.offline; - else if (disabled) - return CommonState.disabled; - else - return CommonState.empty; - } - - /** - * @param account - * @return Color drawable level or default colors if account was not found. - */ - public int getColorLevel(String account) { - AccountItem accountItem = getAccount(account); - int colorIndex; - if (accountItem == null) - return 0; - else - colorIndex = accountItem.getColorIndex() % colors; - if (colorIndex < 0) - colorIndex += colors; - return colorIndex; - } - - /** - * @return Number of different account colors. - */ - public int getColorCount() { - return colors; - } - - private boolean hasSameBareAddress(String account) { - String bareAddress = Jid.getBareAddress(account); - for (AccountItem check : accountItems.values()) - if (!check.getAccount().equals(account) - && Jid.getBareAddress(check.getAccount()).equals( - bareAddress)) - return true; - return false; - } - - private boolean hasSameProtocol(String account) { - AccountProtocol protocol = getAccount(account).getConnectionSettings() - .getProtocol(); - for (AccountItem check : accountItems.values()) - if (!check.getAccount().equals(account) - && check.getConnectionSettings().getProtocol() == protocol) - return true; - return false; - } - - /** - * @param account - * @return Verbose account name. - */ - public String getVerboseName(String account) { - AccountItem accountItem = getAccount(account); - if (accountItem == null) - return account; - if (accountItem.getConnectionSettings().getProtocol().isOAuth()) { - String jid = OAuthManager.getInstance().getAssignedJid(account); - AccountProtocol accountProtocol = accountItem - .getConnectionSettings().getProtocol(); - String name; - if (jid == null) { - if (hasSameProtocol(account)) - name = accountItem.getConnectionSettings().getUserName(); - else - return application.getString(accountProtocol - .getNameResource()); - - } else { - name = Jid.getBareAddress(jid); - if (!hasSameBareAddress(jid)) - return name; - } - return application.getString(accountProtocol.getShortResource()) - + " - " + name; - } else { - if (hasSameBareAddress(account)) - return account; - else - return Jid.getBareAddress(account); - } - } - - /** - * @param account - * @return Account vCard based nick name or verbose name if nick is not - * specified. - */ - public String getNickName(String account) { - String jid = OAuthManager.getInstance().getAssignedJid(account); - String result = VCardManager.getInstance().getName( - Jid.getBareAddress(jid)); - if ("".equals(result)) - return getVerboseName(account); - else - return result; - } - - /** - * Sets status for account. - * - * @param account - * @param statusMode - * @param statusText - */ - public void setStatus(String account, StatusMode statusMode, - String statusText) { - addSavedStatus(statusMode, statusText); - AccountItem accountItem = getAccount(account); - setStatus(accountItem, statusMode, statusText); - try { - PresenceManager.getInstance().resendPresence(account); - } catch (NetworkException e) { - } - boolean found = false; - for (AccountItem check : accountItems.values()) - if (check.isEnabled() - && SettingsManager.statusMode() == check.getRawStatusMode()) { - found = true; - break; - } - if (!found) - SettingsManager.setStatusMode(statusMode); - found = false; - for (AccountItem check : accountItems.values()) - if (check.isEnabled() - && SettingsManager.statusText().equals( - check.getStatusText())) { - found = true; - break; - } - if (!found) - SettingsManager.setStatusText(statusText); - onAccountChanged(account); - } - - boolean isAway() { - return away; - } - - boolean isXa() { - return xa; - } - - /** - * Set status mode to away. - * - * If we are already away or xa, do nothing. - */ - public void goAway() { - if (away || xa) - return; - away = true; - resendPresence(); - } - - /** - * Set status mode to xa. - * - * If we are already xa, do nothing. - */ - public void goXa() { - if (xa) - return; - xa = true; - resendPresence(); - } - - /** - * Restore status mode to the state that was before we go away. - * - * If we are already waked up, do nothing. - */ - public void wakeUp() { - if (!away && !xa) - return; - away = false; - xa = false; - resendPresence(); - } - - /** - * Sends new presence information for all accounts. - */ - public void resendPresence() { - for (AccountItem accountItem : accountItems.values()) - try { - PresenceManager.getInstance().resendPresence( - accountItem.getAccount()); - } catch (NetworkException e) { - } - } - - /** - * Sets status for account. - * - * @param account - * @param statusMode - * @param statusText - */ - private void setStatus(AccountItem accountItem, StatusMode statusMode, - String statusText) { - boolean changed = accountItem.isEnabled() - && accountItem.getRawStatusMode().isOnline() != statusMode - .isOnline(); - accountItem.setStatus(statusMode, statusText); - if (changed && statusMode.isOnline()) - onAccountOnline(accountItem); - accountItem.updateConnection(true); - if (changed && !statusMode.isOnline()) - onAccountOffline(accountItem); - requestToWriteAccount(accountItem); - } - - /** - * Sets status for all accounts. - * - * @param statusMode - * @param statusText - * can be null if value was not changed. - */ - public void setStatus(StatusMode statusMode, String statusText) { - SettingsManager.setStatusMode(statusMode); - if (statusText != null) { - addSavedStatus(statusMode, statusText); - SettingsManager.setStatusText(statusText); - } - for (AccountItem accountItem : accountItems.values()) { - setStatus(accountItem, statusMode, - statusText == null ? accountItem.getStatusText() - : statusText); - } - resendPresence(); - onAccountsChanged(new ArrayList(AccountManager.getInstance() - .getAllAccounts())); - } - - /** - * Save status in presets. - * - * @param statusMode - * @param statusText - */ - private void addSavedStatus(final StatusMode statusMode, - final String statusText) { - SavedStatus savedStatus = new SavedStatus(statusMode, statusText); - if (savedStatuses.contains(savedStatus)) - return; - savedStatuses.add(savedStatus); - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - StatusTable.getInstance().write(statusMode, statusText); - } - }); - } - - /** - * Remove status from presets. - * - * @param statusMode - * @param statusText - */ - public void removeSavedStatus(final SavedStatus savedStatus) { - if (!savedStatuses.remove(savedStatus)) - return; - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - StatusTable.getInstance().remove(savedStatus.getStatusMode(), - savedStatus.getStatusText()); - } - }); - } - - /** - * Clear list of status presets. - */ - public void clearSavedStatuses() { - savedStatuses.clear(); - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - StatusTable.getInstance().clear(); - } - }); - } - - /** - * @return List of preset statuses. - */ - public Collection getSavedStatuses() { - return Collections.unmodifiableCollection(savedStatuses); - } - - /** - * @return Selected account to show contacts. null if - *

    - *
  • there is no selected account,
  • - *
  • selected account does not exists or disabled,
  • - *
  • Group by account is enabled.
  • - *
- */ - public String getSelectedAccount() { - if (SettingsManager.contactsShowAccounts()) - return null; - String selected = SettingsManager.contactsSelectedAccount(); - if (enabledAccounts.contains(selected)) - return selected; - return null; - } - - public void removeAuthorizationError(String account) { - authorizationErrorProvider.remove(account); - } - - public void addAuthenticationError(String account) { - authorizationErrorProvider.add(new AccountAuthorizationError(account), - true); - } - - public void removePasswordRequest(String account) { - passwordRequestProvider.remove(account); - } - - public void addPasswordRequest(String account) { - passwordRequestProvider.add(new PasswordRequest(account), true); - } - - public void onAccountChanged(String account) { - Collection accounts = new ArrayList(1); - accounts.add(account); - onAccountsChanged(accounts); - } - - public void onAccountsChanged(final Collection accounts) { - Application.getInstance().runOnUiThread(new Runnable() { - @Override - public void run() { - for (OnAccountChangedListener accountListener : Application - .getInstance().getUIListeners( - OnAccountChangedListener.class)) - accountListener.onAccountsChanged(accounts); - } - }); - } - - public void onAccountEnabled(AccountItem accountItem) { - for (OnAccountEnabledListener listener : application - .getManagers(OnAccountEnabledListener.class)) - listener.onAccountEnabled(accountItem); - } - - public void onAccountOnline(AccountItem accountItem) { - for (OnAccountOnlineListener listener : application - .getManagers(OnAccountOnlineListener.class)) - listener.onAccountOnline(accountItem); - } - - public void onAccountOffline(AccountItem accountItem) { - accountItem.clearPassword(); - for (OnAccountOfflineListener listener : application - .getManagers(OnAccountOfflineListener.class)) - listener.onAccountOffline(accountItem); - } - - public void onAccountDisabled(AccountItem accountItem) { - for (OnAccountDisabledListener listener : application - .getManagers(OnAccountDisabledListener.class)) - listener.onAccountDisabled(accountItem); - } - - @Override - public void onWipe() { - AccountTable.getInstance().wipe(); - } - -} + private final static AccountManager instance; + + static { + instance = new AccountManager(); + Application.getInstance().addManager(instance); + } + + /** + * List of account presets. + */ + private final List accountTypes; + /** + * List of saved statuses. + */ + private final Collection savedStatuses; + /** + * Number of different account colors. + */ + private final int colors; + /** + * List of accounts. + */ + private final Map accountItems; + /** + * List of enabled account. + */ + private final Collection enabledAccounts; + private final BaseAccountNotificationProvider authorizationErrorProvider; + + private final BaseAccountNotificationProvider passwordRequestProvider; + + private final Application application; + /** + * Whether away status mode is enabled. + */ + private boolean away; + /** + * Whether extended away mode is enabled. + */ + private boolean xa; + + private AccountManager() { + this.application = Application.getInstance(); + accountItems = new HashMap<>(); + enabledAccounts = new HashSet<>(); + savedStatuses = new ArrayList<>(); + authorizationErrorProvider = new BaseAccountNotificationProvider<>(R.drawable.ic_stat_error); + passwordRequestProvider = new BaseAccountNotificationProvider<>(R.drawable.ic_stat_add_circle); + + colors = application.getResources().getIntArray(R.array.account_color_names).length; + + TypedArray types = application.getResources().obtainTypedArray(R.array.account_types); + accountTypes = new ArrayList<>(); + for (int index = 0; index < types.length(); index++) { + int id = types.getResourceId(index, 0); + TypedArray values = application.getResources().obtainTypedArray(id); + AccountProtocol protocol = AccountProtocol.valueOf(values.getString(0)); + if (Build.VERSION.SDK_INT < 8 && protocol == AccountProtocol.wlm) { + values.recycle(); + continue; + } + ArrayList servers = new ArrayList<>(); + servers.add(values.getString(9)); + for (int i = 10; i < values.length(); i++) { + servers.add(values.getString(i)); + } + accountTypes.add(new AccountType(id, protocol, values.getString(1), + values.getString(2), values.getString(3), values.getDrawable(4), + values.getBoolean(5, false), values.getString(6), values.getInt(7, 5222), + values.getBoolean(8, false), servers)); + values.recycle(); + } + types.recycle(); + away = false; + xa = false; + } + + public static AccountManager getInstance() { + return instance; + } + + @Override + public void onLoad() { + final Collection savedStatuses = new ArrayList<>(); + final Collection accountItems = new ArrayList<>(); + Cursor cursor = StatusTable.getInstance().list(); + try { + if (cursor.moveToFirst()) { + do { + savedStatuses.add(new SavedStatus(StatusTable.getStatusMode(cursor), + StatusTable.getStatusText(cursor))); + } while (cursor.moveToNext()); + } + } finally { + cursor.close(); + } + + cursor = AccountTable.getInstance().list(); + try { + if (cursor.moveToFirst()) { + do { + AccountItem accountItem = new AccountItem( + AccountTable.getProtocol(cursor), + AccountTable.isCustom(cursor), + AccountTable.getHost(cursor), + AccountTable.getPort(cursor), + AccountTable.getServerName(cursor), + AccountTable.getUserName(cursor), + AccountTable.getResource(cursor), + AccountTable.isStorePassword(cursor), + AccountTable.getPassword(cursor), + AccountTable.getColorIndex(cursor), + AccountTable.getPriority(cursor), + AccountTable.getStatusMode(cursor), + AccountTable.getStatusText(cursor), + AccountTable.isEnabled(cursor), + AccountTable.isSaslEnabled(cursor), + AccountTable.getTLSMode(cursor), + AccountTable.isCompression(cursor), + AccountTable.getProxyType(cursor), + AccountTable.getProxyHost(cursor), + AccountTable.getProxyPort(cursor), + AccountTable.getProxyUser(cursor), + AccountTable.getProxyPassword(cursor), + AccountTable.isSyncable(cursor), + AccountTable.getKeyPair(cursor), + AccountTable.getLastSync(cursor), + AccountTable.getArchiveMode(cursor)); + accountItem.setId(AccountTable.getId(cursor)); + accountItems.add(accountItem); + } while (cursor.moveToNext()); + } + } finally { + cursor.close(); + } + + Application.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + onLoaded(savedStatuses, accountItems); + } + }); + } + + private void onLoaded(Collection savedStatuses, Collection accountItems) { + this.savedStatuses.addAll(savedStatuses); + for (AccountItem accountItem : accountItems) { + addAccount(accountItem); + } + NotificationManager.getInstance().registerNotificationProvider(authorizationErrorProvider); + NotificationManager.getInstance().registerNotificationProvider(passwordRequestProvider); + } + + private void addAccount(AccountItem accountItem) { + accountItems.put(accountItem.getAccount(), accountItem); + if (accountItem.isEnabled()) { + enabledAccounts.add(accountItem.getAccount()); + } + for (OnAccountAddedListener listener : application.getManagers(OnAccountAddedListener.class)) { + listener.onAccountAdded(accountItem); + } + if (accountItem.isEnabled()) { + onAccountEnabled(accountItem); + if (accountItem.getRawStatusMode().isOnline()) { + onAccountOnline(accountItem); + } + } + onAccountChanged(accountItem.getAccount()); + } + + /** + * @return List of supported account types. + */ + public List getAccountTypes() { + return accountTypes; + } + + /** + * @return Next color index for the next account. + */ + int getNextColorIndex() { + int[] count = new int[colors]; + for (AccountItem accountItem : accountItems.values()) { + count[accountItem.getColorIndex() % colors] += 1; + } + int result = 0; + int value = count[0]; + for (int index = 0; index < count.length; index++) { + if (count[index] < value) { + result = index; + } + } + return result; + } + + /** + * @param account full jid. + * @return Specified account or null if account doesn't exists. + */ + public AccountItem getAccount(String account) { + return accountItems.get(account); + } + + /** + * Save account item to database. + * + * @param accountItem + */ + void requestToWriteAccount(final AccountItem accountItem) { + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + accountItem.setId(AccountTable.getInstance().write(accountItem.getId(), accountItem)); + } + }); + } + + /** + * Creates new account and starts connection. + */ + private AccountItem addAccount(AccountProtocol protocol, boolean custom, String host, int port, + String serverName, String userName, boolean storePassword, + String password, String resource, int color, int priority, + StatusMode statusMode, String statusText, boolean enabled, + boolean saslEnabled, TLSMode tlsMode, boolean compression, + ProxyType proxyType, String proxyHost, int proxyPort, + String proxyUser, String proxyPassword, boolean syncable, + KeyPair keyPair, Date lastSync, ArchiveMode archiveMode, + boolean registerNewAccount) { + + AccountItem accountItem = new AccountItem(protocol, custom, host, port, serverName, userName, + resource, storePassword, password, color, priority, statusMode, statusText, enabled, + saslEnabled, tlsMode, compression, proxyType, proxyHost, proxyPort, proxyUser, + proxyPassword, syncable, keyPair, lastSync, archiveMode); + + if (registerNewAccount) { + // TODO: attempt to register account, if that fails return null; + accountItem.registerAccount(); + // return(null); + } + requestToWriteAccount(accountItem); + addAccount(accountItem); + accountItem.updateConnection(true); + return accountItem; + } + + /** + * Creates new account. + * + * @param user full or bare jid. + * @param password + * @param accountType xmpp account type can be replaced depend on server part. + * @param syncable + * @param storePassword + * @param useOrbot + * @return assigned account name. + * @throws NetworkException if user or server part are invalid. + */ + public String addAccount(String user, String password, AccountType accountType, boolean syncable, + boolean storePassword, boolean useOrbot, boolean registerNewAccount) + throws NetworkException { + if (accountType.getProtocol().isOAuth()) { + int index = 1; + while (true) { + user = String.valueOf(index); + boolean found = false; + for (AccountItem accountItem : accountItems.values()) { + if (accountItem.getConnectionSettings().getServerName() + .equals(accountType.getFirstServer()) + && accountItem.getConnectionSettings().getUserName().equals(user)) { + found = true; + break; + } + } + if (!found) { + break; + } + index++; + } + } + + if (user == null) { + throw new NetworkException(R.string.EMPTY_USER_NAME); + } + + if (!user.contains("@")) { + if ("".equals(accountType.getFirstServer())) { + throw new NetworkException(R.string.EMPTY_SERVER_NAME); + } else { + user += "@" + accountType.getFirstServer(); + } + } + + String serverName = StringUtils.parseServer(user); + String userName = StringUtils.parseName(user); + String resource = StringUtils.parseResource(user); + String host = accountType.getHost(); + int port = accountType.getPort(); + boolean tlsRequired = accountType.isTLSRequired(); + if (useOrbot) { + tlsRequired = true; + } + + if ("".equals(serverName)) { + throw new NetworkException(R.string.EMPTY_SERVER_NAME); + } else if (!accountType.isAllowServer() && !serverName.equals(accountType.getFirstServer())) { + throw new NetworkException(R.string.INCORRECT_USER_NAME); + } + + if ("".equals(userName)) { + throw new NetworkException(R.string.EMPTY_USER_NAME); + } + if ("".equals(resource)) { + resource = "android" + StringUtils.randomString(8); + } + + if (accountType.getId() == R.array.account_type_xmpp) { + host = serverName; + for (AccountType check : accountTypes) { + if (check.getServers().contains(serverName)) { + accountType = check; + host = check.getHost(); + port = check.getPort(); + tlsRequired = check.isTLSRequired(); + break; + } + } + } + + AccountItem accountItem; + while(true) { + if (getAccount(userName + '@' + serverName + '/' + resource) == null) { + break; + } + resource = "android" + StringUtils.randomString(8); + } + + + boolean useCustomHost = application.getResources().getBoolean(R.bool.account_use_custom_host_default); + if (accountType.getProtocol() == AccountProtocol.gtalk) { + useCustomHost = true; + } + + boolean useCompression = application.getResources().getBoolean(R.bool.account_use_compression_default); + + ArchiveMode archiveMode = ArchiveMode.valueOf(application.getString(R.string.account_archive_mode_default_value)); + + accountItem = addAccount(accountType.getProtocol(), useCustomHost, host, port, serverName, userName, + storePassword, password, resource, getNextColorIndex(), 0, StatusMode.available, + SettingsManager.statusText(), true, true, tlsRequired ? TLSMode.required : TLSMode.enabled, + useCompression, useOrbot ? ProxyType.orbot : ProxyType.none, "localhost", 8080, + "", "", syncable, null, null, archiveMode, registerNewAccount); + if (accountItem == null) { + throw new NetworkException(R.string.ACCOUNT_REGISTER_FAILED); + } + + onAccountChanged(accountItem.getAccount()); + if (accountItems.size() > 1 && SettingsManager.contactsEnableShowAccounts()) { + SettingsManager.enableContactsShowAccount(); + } + return accountItem.getAccount(); + } + + /** + * Remove user`s account. Don't call any callbacks. + * + * @param account + */ + private void removeAccountWithoutCallback(final String account) { + final AccountItem accountItem = getAccount(account); + boolean wasEnabled = accountItem.isEnabled(); + accountItem.setEnabled(false); + accountItem.updateConnection(true); + if (wasEnabled) { + if (accountItem.getRawStatusMode().isOnline()) { + onAccountOffline(accountItem); + } + onAccountDisabled(accountItem); + } + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + AccountTable.getInstance().remove(account, accountItem.getId()); + } + }); + accountItems.remove(account); + enabledAccounts.remove(account); + for (OnAccountRemovedListener listener : application.getManagers(OnAccountRemovedListener.class)) { + listener.onAccountRemoved(accountItem); + } + removeAuthorizationError(account); + } + + /** + * Remove user`s account. + * + * @param account + */ + public void removeAccount(String account) { + removeAccountWithoutCallback(account); + onAccountChanged(account); + } + + /** + * Update user`s account. + *

+ * It will reconnect to the server if changes was made. + *

+ * It will remove old account and create new one if full jid was changed. + * + * @param account full source jid + * @param host + * @param port + * @param serverName + * @param userName + * @param storePassword + * @param password + * @param resource + * @param priority + * @param enabled + * @param saslEnabled + * @param tlsMode + * @param compression + * @param syncable + * @param archiveMode + */ + public void updateAccount(String account, boolean custom, String host, int port, String serverName, + String userName, boolean storePassword, String password, String resource, + int priority, boolean enabled, boolean saslEnabled, TLSMode tlsMode, + boolean compression, ProxyType proxyType, String proxyHost, int proxyPort, + String proxyUser, String proxyPassword, boolean syncable, + ArchiveMode archiveMode, int colorIndex) { + AccountItem result; + AccountItem accountItem = getAccount(account); + + if (accountItem.getConnectionSettings().getServerName().equals(serverName) + && accountItem.getConnectionSettings().getUserName().equals(userName) + && accountItem.getConnectionSettings().getResource().equals(resource)) { + result = accountItem; + + result.setColorIndex(colorIndex); + + boolean reconnect = false; + if (accountItem.getConnectionSettings().isCustom() != custom + || !accountItem.getConnectionSettings().getHost().equals(host) + || accountItem.getConnectionSettings().getPort() != port + || !accountItem.getConnectionSettings().getPassword().equals(password) + || accountItem.getConnectionSettings().getTlsMode() != tlsMode + || accountItem.getConnectionSettings().isSaslEnabled() != saslEnabled + || accountItem.getConnectionSettings().useCompression() != compression + || accountItem.getConnectionSettings().getProxyType() != proxyType + || !accountItem.getConnectionSettings().getProxyHost().equals(proxyHost) + || accountItem.getConnectionSettings().getProxyPort() != proxyPort + || !accountItem.getConnectionSettings().getProxyUser().equals(proxyUser) + || !accountItem.getConnectionSettings().getProxyPassword().equals(proxyPassword)) { + result.updateConnectionSettings(custom, host, port, password, saslEnabled, tlsMode, + compression, proxyType, proxyHost, proxyPort, proxyUser, proxyPassword); + reconnect = true; + } + if (result.isSyncable() != syncable) { + result.setSyncable(syncable); + for (OnAccountSyncableChangedListener listener : + application.getManagers(OnAccountSyncableChangedListener.class)) { + listener.onAccountSyncableChanged(result); + } + } + result.setStorePassword(storePassword); + boolean changed = result.isEnabled() != enabled; + result.setEnabled(enabled); + if (result.getPriority() != priority) { + result.setPriority(priority); + try { + PresenceManager.getInstance().resendPresence(account); + } catch (NetworkException e) { + } + } + if (result.getArchiveMode() != archiveMode) { + reconnect = (result.getArchiveMode() == ArchiveMode.server) != (archiveMode == ArchiveMode.server); + result.setArchiveMode(archiveMode); + for (OnAccountArchiveModeChangedListener listener : + application.getManagers(OnAccountArchiveModeChangedListener.class)) { + listener.onAccountArchiveModeChanged(result); + } + } + if (changed && enabled) { + enabledAccounts.add(account); + onAccountEnabled(result); + if (result.getRawStatusMode().isOnline()) { + onAccountOnline(result); + } + } + if (changed || reconnect) { + result.updateConnection(true); + result.forceReconnect(); + } + if (changed && !enabled) { + enabledAccounts.remove(account); + if (result.getRawStatusMode().isOnline()) { + onAccountOffline(result); + } + onAccountDisabled(result); + } + requestToWriteAccount(result); + } else { + StatusMode statusMode = accountItem.getRawStatusMode(); + String statusText = accountItem.getStatusText(); + AccountProtocol protocol = accountItem.getConnectionSettings().getProtocol(); + KeyPair keyPair = accountItem.getKeyPair(); + Date lastSync = accountItem.getLastSync(); + removeAccountWithoutCallback(account); + result = addAccount(protocol, custom, host, port, serverName, userName, storePassword, + password, resource, colorIndex, priority, statusMode, statusText, enabled, + saslEnabled, tlsMode, compression, proxyType, proxyHost, proxyPort, proxyUser, + proxyPassword, syncable, keyPair, lastSync, archiveMode, false); + } + onAccountChanged(result.getAccount()); + } + + public void setKeyPair(String account, KeyPair keyPair) { + AccountItem accountItem = getAccount(account); + accountItem.setKeyPair(keyPair); + requestToWriteAccount(accountItem); + } + + public void setLastSync(String account, Date lastSync) { + AccountItem accountItem = getAccount(account); + accountItem.setLastSync(lastSync); + requestToWriteAccount(accountItem); + } + + public void setSyncable(String account, boolean syncable) { + AccountItem accountItem = getAccount(account); + ConnectionSettings connectionSettings = accountItem.getConnectionSettings(); + updateAccount( + account, + connectionSettings.isCustom(), + connectionSettings.getHost(), + connectionSettings.getPort(), + connectionSettings.getServerName(), + connectionSettings.getUserName(), + accountItem.isStorePassword(), + connectionSettings.getPassword(), + connectionSettings.getResource(), + accountItem.getPriority(), + accountItem.isEnabled(), + connectionSettings.isSaslEnabled(), + connectionSettings.getTlsMode(), + connectionSettings.useCompression(), + connectionSettings.getProxyType(), + connectionSettings.getProxyHost(), + connectionSettings.getProxyPort(), + connectionSettings.getProxyUser(), + connectionSettings.getProxyPassword(), + syncable, + accountItem.getArchiveMode(), + accountItem.getColorIndex() + ); + } + + public void setPassword(String account, boolean storePassword, String password) { + AccountItem accountItem = getAccount(account); + ConnectionSettings connectionSettings = accountItem.getConnectionSettings(); + updateAccount( + account, + connectionSettings.isCustom(), + connectionSettings.getHost(), + connectionSettings.getPort(), + connectionSettings.getServerName(), + connectionSettings.getUserName(), + storePassword, + password, + connectionSettings.getResource(), + accountItem.getPriority(), + accountItem.isEnabled(), + connectionSettings.isSaslEnabled(), + connectionSettings.getTlsMode(), + connectionSettings.useCompression(), + connectionSettings.getProxyType(), + connectionSettings.getProxyHost(), + connectionSettings.getProxyPort(), + connectionSettings.getProxyUser(), + connectionSettings.getProxyPassword(), + accountItem.isSyncable(), + accountItem.getArchiveMode(), + accountItem.getColorIndex() + ); + } + + public void setArchiveMode(String account, ArchiveMode archiveMode) { + AccountItem accountItem = AccountManager.getInstance().getAccount(account); + ConnectionSettings connectionSettings = accountItem.getConnectionSettings(); + AccountManager.getInstance().updateAccount( + account, + connectionSettings.isCustom(), + connectionSettings.getHost(), + connectionSettings.getPort(), + connectionSettings.getServerName(), + connectionSettings.getUserName(), + accountItem.isStorePassword(), + connectionSettings.getPassword(), + connectionSettings.getResource(), + accountItem.getPriority(), + accountItem.isEnabled(), + connectionSettings.isSaslEnabled(), + connectionSettings.getTlsMode(), + connectionSettings.useCompression(), + connectionSettings.getProxyType(), + connectionSettings.getProxyHost(), + connectionSettings.getProxyPort(), + connectionSettings.getProxyUser(), + connectionSettings.getProxyPassword(), + accountItem.isSyncable(), + archiveMode, + accountItem.getColorIndex() + ); + } + + public ArchiveMode getArchiveMode(String account) { + AccountItem accountItem = getAccount(account); + if (accountItem == null) { + return ArchiveMode.available; + } + return accountItem.getArchiveMode(); + } + + /** + * @return List of enabled accounts. + */ + public Collection getAccounts() { + return Collections.unmodifiableCollection(enabledAccounts); + } + + /** + * @return List of all accounts including disabled. + */ + public Collection getAllAccounts() { + return Collections.unmodifiableCollection(accountItems.keySet()); + } + + public CommonState getCommonState() { + boolean disabled = false; + boolean offline = false; + boolean waiting = false; + boolean connecting = false; + boolean roster = false; + boolean online = false; + + for (AccountItem accountItem : accountItems.values()) { + ConnectionState state = accountItem.getState(); + if (state == ConnectionState.connected) { + online = true; + } + if (RosterManager.getInstance().isRosterReceived(accountItem.getAccount())) { + roster = true; + } + if (state == ConnectionState.connecting || state == ConnectionState.authentication) { + connecting = true; + } + if (state == ConnectionState.waiting) { + waiting = true; + } + if (accountItem.isEnabled()) { + offline = true; + } + disabled = true; + } + + if (online) { + return CommonState.online; + } else if (roster) { + return CommonState.roster; + } else if (connecting) { + return CommonState.connecting; + } + + if (waiting) { + return CommonState.waiting; + } else if (offline) { + return CommonState.offline; + } else if (disabled) { + return CommonState.disabled; + } else { + return CommonState.empty; + } + } + + /** + * @param account + * @return Color drawable level or default colors if account was not found. + */ + public int getColorLevel(String account) { + AccountItem accountItem = getAccount(account); + int colorIndex; + + if (accountItem == null) { + return 0; + } else { + colorIndex = accountItem.getColorIndex() % colors; + } + + if (colorIndex < 0) { + colorIndex += colors; + } + return colorIndex; + } + + /** + * @return Number of different account colors. + */ + public int getColorCount() { + return colors; + } + + private boolean hasSameBareAddress(String account) { + String bareAddress = Jid.getBareAddress(account); + for (AccountItem check : accountItems.values()) { + if (!check.getAccount().equals(account) + && Jid.getBareAddress(check.getAccount()).equals(bareAddress)) { + return true; + } + } + return false; + } + + private boolean hasSameProtocol(String account) { + AccountProtocol protocol = getAccount(account).getConnectionSettings().getProtocol(); + for (AccountItem check : accountItems.values()) { + if (!check.getAccount().equals(account) + && check.getConnectionSettings().getProtocol() == protocol) { + return true; + } + } + return false; + } + + /** + * @param account + * @return Verbose account name. + */ + public String getVerboseName(String account) { + AccountItem accountItem = getAccount(account); + if (accountItem == null) { + return account; + } + if (accountItem.getConnectionSettings().getProtocol().isOAuth()) { + String jid = OAuthManager.getInstance().getAssignedJid(account); + AccountProtocol accountProtocol = accountItem.getConnectionSettings().getProtocol(); + String name; + if (jid == null) { + if (hasSameProtocol(account)) { + name = accountItem.getConnectionSettings().getUserName(); + } else { + return application.getString(accountProtocol.getNameResource()); + } + } else { + name = Jid.getBareAddress(jid); + if (!hasSameBareAddress(jid)) { + return name; + } + } + return application.getString(accountProtocol.getShortResource()) + " - " + name; + } else { + if (hasSameBareAddress(account)) { + return account; + } else { + return Jid.getBareAddress(account); + } + } + } + + /** + * @param account + * @return Account vCard based nick name or verbose name if nick is not + * specified. + */ + public String getNickName(String account) { + String jid = OAuthManager.getInstance().getAssignedJid(account); + String result = VCardManager.getInstance().getName(Jid.getBareAddress(jid)); + if ("".equals(result)) { + return getVerboseName(account); + } else { + return result; + } + } + + /** + * Sets status for account. + * + * @param account + * @param statusMode + * @param statusText + */ + public void setStatus(String account, StatusMode statusMode, String statusText) { + if (statusText != null && !statusText.trim().isEmpty()) { + addSavedStatus(statusMode, statusText); + } + + AccountItem accountItem = getAccount(account); + setStatus(accountItem, statusMode, statusText); + try { + PresenceManager.getInstance().resendPresence(account); + } catch (NetworkException e) { + } + boolean found = false; + for (AccountItem check : accountItems.values()) { + if (check.isEnabled() && SettingsManager.statusMode() == check.getRawStatusMode()) { + found = true; + break; + } + } + if (!found) { + SettingsManager.setStatusMode(statusMode); + } + found = false; + for (AccountItem check : accountItems.values()) { + if (check.isEnabled() && SettingsManager.statusText().equals(check.getStatusText())) { + found = true; + break; + } + } + if (!found) { + SettingsManager.setStatusText(statusText); + } + onAccountChanged(account); + } + + boolean isAway() { + return away; + } + + boolean isXa() { + return xa; + } + + /** + * Set status mode to away. + *

+ * If we are already away or xa, do nothing. + */ + public void goAway() { + if (away || xa) { + return; + } + away = true; + resendPresence(); + } + + /** + * Set status mode to xa. + *

+ * If we are already xa, do nothing. + */ + public void goXa() { + if (xa) { + return; + } + xa = true; + resendPresence(); + } + + /** + * Restore status mode to the state that was before we go away. + *

+ * If we are already waked up, do nothing. + */ + public void wakeUp() { + if (!away && !xa) { + return; + } + away = false; + xa = false; + resendPresence(); + } + + /** + * Sends new presence information for all accounts. + */ + public void resendPresence() { + for (AccountItem accountItem : accountItems.values()) { + try { + PresenceManager.getInstance().resendPresence(accountItem.getAccount()); + } catch (NetworkException e) { + } + } + } + + /** + * Sets status for account. + * + * @param account + * @param statusMode + * @param statusText + */ + private void setStatus(AccountItem accountItem, StatusMode statusMode, String statusText) { + boolean changed = accountItem.isEnabled() + && accountItem.getRawStatusMode().isOnline() != statusMode.isOnline(); + accountItem.setStatus(statusMode, statusText); + if (changed && statusMode.isOnline()) { + onAccountOnline(accountItem); + } + accountItem.updateConnection(true); + if (changed && !statusMode.isOnline()) { + onAccountOffline(accountItem); + } + requestToWriteAccount(accountItem); + } + + /** + * Sets status for all accounts. + * + * @param statusMode + * @param statusText can be null if value was not changed. + */ + public void setStatus(StatusMode statusMode, String statusText) { + SettingsManager.setStatusMode(statusMode); + + if (statusText != null) { + addSavedStatus(statusMode, statusText); + SettingsManager.setStatusText(statusText); + } + + for (AccountItem accountItem : accountItems.values()) { + setStatus(accountItem, statusMode, + statusText == null ? accountItem.getStatusText() : statusText); + } + + resendPresence(); + onAccountsChanged(new ArrayList<>(AccountManager.getInstance().getAllAccounts())); + } + + /** + * Save status in presets. + * + * @param statusMode + * @param statusText + */ + private void addSavedStatus(final StatusMode statusMode, final String statusText) { + SavedStatus savedStatus = new SavedStatus(statusMode, statusText); + if (savedStatuses.contains(savedStatus)) { + return; + } + savedStatuses.add(savedStatus); + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + StatusTable.getInstance().write(statusMode, statusText); + } + }); + } + + /** + * Remove status from presets. + * + * @param statusMode + * @param statusText + */ + public void removeSavedStatus(final SavedStatus savedStatus) { + if (!savedStatuses.remove(savedStatus)) { + return; + } + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + StatusTable.getInstance().remove(savedStatus.getStatusMode(), + savedStatus.getStatusText()); + } + }); + } + + /** + * Clear list of status presets. + */ + public void clearSavedStatuses() { + savedStatuses.clear(); + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + StatusTable.getInstance().clear(); + } + }); + } + + /** + * @return List of preset statuses. + */ + public Collection getSavedStatuses() { + return Collections.unmodifiableCollection(savedStatuses); + } + + /** + * @return Selected account to show contacts. null if + *

    + *
  • there is no selected account,
  • + *
  • selected account does not exists or disabled,
  • + *
  • Group by account is enabled.
  • + *
+ */ + public String getSelectedAccount() { + if (SettingsManager.contactsShowAccounts()) { + return null; + } + String selected = SettingsManager.contactsSelectedAccount(); + if (enabledAccounts.contains(selected)) { + return selected; + } + return null; + } + + public void removeAuthorizationError(String account) { + authorizationErrorProvider.remove(account); + } + + public void addAuthenticationError(String account) { + authorizationErrorProvider.add(new AccountAuthorizationError(account), true); + } + + public void removePasswordRequest(String account) { + passwordRequestProvider.remove(account); + } + + public void addPasswordRequest(String account) { + passwordRequestProvider.add(new PasswordRequest(account), true); + } + + public void onAccountChanged(String account) { + Collection accounts = new ArrayList<>(1); + accounts.add(account); + onAccountsChanged(accounts); + } + + public void onAccountsChanged(final Collection accounts) { + Application.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + for (OnAccountChangedListener accountListener + : Application.getInstance().getUIListeners(OnAccountChangedListener.class)) { + accountListener.onAccountsChanged(accounts); + } + } + }); + } + + public void onAccountEnabled(AccountItem accountItem) { + for (OnAccountEnabledListener listener : application.getManagers(OnAccountEnabledListener.class)) { + listener.onAccountEnabled(accountItem); + } + } + + public void onAccountOnline(AccountItem accountItem) { + for (OnAccountOnlineListener listener : application.getManagers(OnAccountOnlineListener.class)) { + listener.onAccountOnline(accountItem); + } + } + + public void onAccountOffline(AccountItem accountItem) { + accountItem.clearPassword(); + for (OnAccountOfflineListener listener : application.getManagers(OnAccountOfflineListener.class)) { + listener.onAccountOffline(accountItem); + } + } + + public void onAccountDisabled(AccountItem accountItem) { + for (OnAccountDisabledListener listener : application.getManagers(OnAccountDisabledListener.class)) { + listener.onAccountDisabled(accountItem); + } + } + + @Override + public void onWipe() { + AccountTable.getInstance().wipe(); + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/data/account/AccountProtocol.java b/app/src/main/java/com/xabber/android/data/account/AccountProtocol.java index caee5ba0d6..21cae2cea3 100644 --- a/app/src/main/java/com/xabber/android/data/account/AccountProtocol.java +++ b/app/src/main/java/com/xabber/android/data/account/AccountProtocol.java @@ -1,77 +1,72 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data.account; -import com.xabber.androiddev.R; +import com.xabber.android.R; /** * Supported account protocols. - * + * * @author alexander.ivanov - * */ public enum AccountProtocol { - /** - * XMPP protocol. - */ - xmpp, + /** + * XMPP protocol. + */ + xmpp, - /** - * GTalk. - */ - gtalk, + /** + * GTalk. + */ + gtalk, - /** - * Windows Live Messenger. - */ - wlm; + /** + * Windows Live Messenger. + */ + wlm; - /** - * @return Whether protocol support OAuth authorization. - */ - public boolean isOAuth() { - return this == wlm; - } + /** + * @return Whether protocol support OAuth authorization. + */ + public boolean isOAuth() { + return this == wlm; + } - /** - * @return Display name. - */ - public int getNameResource() { - if (this == xmpp) - return R.string.account_type_names_xmpp; - else if (this == gtalk) - return R.string.account_type_names_gtalk; - else if (this == wlm) - return R.string.account_type_names_wlm; - else - throw new UnsupportedOperationException(); - } + /** + * @return Display name. + */ + public int getNameResource() { + if (this == xmpp) + return R.string.xmpp; + else if (this == gtalk) + return R.string.google_talk; + else + throw new UnsupportedOperationException(); + } - /** - * @return Short name. - */ - public int getShortResource() { - if (this == xmpp) - return R.string.account_protocol_xmpp_title; - else if (this == gtalk) - return R.string.account_protocol_gtalk_title; - else if (this == wlm) - return R.string.account_protocol_wlm_title; - else - throw new UnsupportedOperationException(); - } + /** + * @return Short name. + */ + public int getShortResource() { + if (this == xmpp) + return R.string.xmpp; + else if (this == gtalk) + return R.string.google_talk; + else + throw new UnsupportedOperationException(); + } } diff --git a/app/src/main/java/com/xabber/android/data/account/AccountTable.java b/app/src/main/java/com/xabber/android/data/account/AccountTable.java index 6c8f1af2b9..b2207c73b9 100644 --- a/app/src/main/java/com/xabber/android/data/account/AccountTable.java +++ b/app/src/main/java/com/xabber/android/data/account/AccountTable.java @@ -1,29 +1,19 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data.account; -import java.security.KeyFactory; -import java.security.KeyPair; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.PKCS8EncodedKeySpec; -import java.security.spec.X509EncodedKeySpec; -import java.util.Date; - import android.content.ContentValues; import android.content.SharedPreferences.Editor; import android.database.Cursor; @@ -31,515 +21,513 @@ import android.preference.PreferenceManager; import android.provider.BaseColumns; +import com.xabber.android.R; import com.xabber.android.data.AbstractTable; import com.xabber.android.data.Application; import com.xabber.android.data.DatabaseManager; +import com.xabber.android.data.connection.ConnectionSettings; import com.xabber.android.data.connection.ProxyType; import com.xabber.android.data.connection.TLSMode; -import com.xabber.androiddev.R; + +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Date; /** * Storage with account settings. - * + * * @author alexander.ivanov */ class AccountTable extends AbstractTable { - private static final class Fields implements BaseColumns { - private Fields() { - } - - public static final String ENABLED = "enabled"; - public static final String SERVER_NAME = "server_name"; - public static final String USER_NAME = "user_name"; - public static final String PASSWORD = "password"; - public static final String RESOURCE = "resource"; - public static final String PRIORITY = "priority"; - public static final String STATUS_MODE = "status_mode"; - public static final String STATUS_TEXT = "status_text"; - public static final String CUSTOM = "custom"; - public static final String HOST = "host"; - public static final String PORT = "port"; - public static final String SASL_ENABLED = "sasl_enabled"; - public static final String TLS_MODE = "required_tls"; - public static final String COMPRESSION = "compression"; - public static final String COLOR_INDEX = "color_index"; - public static final String PROTOCOL = "protocol"; - public static final String SYNCABLE = "syncable"; - public static final String STORE_PASSWORD = "store_password"; - public static final String PUBLIC_KEY = "public_key"; - public static final String PRIVATE_KEY = "private_key"; - public static final String LAST_SYNC = "last_sync"; - public static final String ARCHIVE_MODE = "archive_mode"; - public static final String PROXY_TYPE = "proxy_type"; - public static final String PROXY_HOST = "proxy_host"; - public static final String PROXY_PORT = "proxy_port"; - public static final String PROXY_USER = "proxy_user"; - public static final String PROXY_PASSWORD = "proxy_password"; - } - - private static final String NAME = "accounts"; - private static final String[] PROJECTION = new String[] { Fields._ID, - Fields.PROTOCOL, Fields.CUSTOM, Fields.HOST, Fields.PORT, - Fields.SERVER_NAME, Fields.USER_NAME, Fields.PASSWORD, - Fields.RESOURCE, Fields.COLOR_INDEX, Fields.PRIORITY, - Fields.STATUS_MODE, Fields.STATUS_TEXT, Fields.ENABLED, - Fields.SASL_ENABLED, Fields.TLS_MODE, Fields.COMPRESSION, - Fields.SYNCABLE, Fields.STORE_PASSWORD, Fields.PUBLIC_KEY, - Fields.PRIVATE_KEY, Fields.LAST_SYNC, Fields.ARCHIVE_MODE, - Fields.PROXY_TYPE, Fields.PROXY_HOST, Fields.PROXY_PORT, - Fields.PROXY_USER, Fields.PROXY_PASSWORD }; - - private final DatabaseManager databaseManager; - - private final static AccountTable instance; - - static { - instance = new AccountTable(DatabaseManager.getInstance()); - DatabaseManager.getInstance().addTable(instance); - } - - public static AccountTable getInstance() { - return instance; - } - - private AccountTable(DatabaseManager databaseManager) { - this.databaseManager = databaseManager; - } - - @Override - public void create(SQLiteDatabase db) { - String sql = "CREATE TABLE " + NAME + " (" + Fields._ID - + " INTEGER PRIMARY KEY," + Fields.PROTOCOL + " TEXT," - + Fields.CUSTOM + " INTEGER," + Fields.HOST + " TEXT," - + Fields.PORT + " INTEGER," + Fields.SERVER_NAME + " TEXT," - + Fields.USER_NAME + " TEXT," + Fields.PASSWORD + " TEXT," - + Fields.RESOURCE + " TEXT," + Fields.COLOR_INDEX + " INTEGER," - + Fields.PRIORITY + " INTEGER," + Fields.STATUS_MODE - + " INTEGER," + Fields.STATUS_TEXT + " TEXT," + Fields.ENABLED - + " INTEGER," + Fields.SASL_ENABLED + " INTEGER," - + Fields.TLS_MODE + " INTEGER," + Fields.COMPRESSION - + " INTEGER," + Fields.SYNCABLE + " INTEGER," - + Fields.STORE_PASSWORD + " INTEGER," + Fields.PUBLIC_KEY - + " BLOB," + Fields.PRIVATE_KEY + " BLOB," + Fields.LAST_SYNC - + " INTEGER," + Fields.ARCHIVE_MODE + " INTEGER," - + Fields.PROXY_TYPE + " INTEGER," + Fields.PROXY_HOST - + " TEXT," + Fields.PROXY_PORT + " INTEGER," - + Fields.PROXY_USER + " TEXT," + Fields.PROXY_PASSWORD - + " TEXT);"; - DatabaseManager.execSQL(db, sql); - } - - @Override - public void migrate(SQLiteDatabase db, int toVersion) { - super.migrate(db, toVersion); - String sql; - switch (toVersion) { - case 3: - DatabaseManager.renameTable(db, "accounts", "accounts_"); - sql = "CREATE TABLE accounts (" + "_id INTEGER PRIMARY KEY," - + "host TEXT," + "port INTEGER," + "server_name TEXT," - + "user_name TEXT," + "password TEXT," + "resource TEXT," - + "color_index INTEGER," + "priority INTEGER," - + "status_mode INTEGER," + "status_text TEXT);"; - DatabaseManager.execSQL(db, sql); - sql = "INSERT INTO accounts SELECT " - + "_id, host, port, server_name, user_name, password, resource, " - + "_id, 0, " + StatusMode.available.ordinal() - + ", '' FROM accounts_;"; - DatabaseManager.execSQL(db, sql); - DatabaseManager.dropTable(db, "accounts_"); - break; - case 9: - sql = "ALTER TABLE accounts ADD COLUMN enabled INTEGER;"; - DatabaseManager.execSQL(db, sql); - sql = "UPDATE accounts SET enabled = 1;"; - DatabaseManager.execSQL(db, sql); - break; - case 21: - sql = "ALTER TABLE accounts ADD COLUMN required_tls INTEGER;"; - DatabaseManager.execSQL(db, sql); - sql = "UPDATE accounts SET required_tls = 0;"; - DatabaseManager.execSQL(db, sql); - break; - case 22: - sql = "ALTER TABLE accounts ADD COLUMN compression INTEGER;"; - DatabaseManager.execSQL(db, sql); - sql = "UPDATE accounts SET compression = 0;"; - DatabaseManager.execSQL(db, sql); - break; - case 30: - sql = "ALTER TABLE accounts ADD COLUMN share_location INTEGER;"; - DatabaseManager.execSQL(db, sql); - sql = "ALTER TABLE accounts ADD COLUMN accept_location INTEGER;"; - DatabaseManager.execSQL(db, sql); - sql = "UPDATE accounts SET share_location = 0, accept_location = 1;"; - DatabaseManager.execSQL(db, sql); - break; - case 31: - sql = "UPDATE accounts SET accept_location = 0;"; - DatabaseManager.execSQL(db, sql); - break; - case 34: - long count = db.compileStatement("SELECT COUNT(*) FROM accounts;") - .simpleQueryForLong(); - Editor editor = PreferenceManager.getDefaultSharedPreferences( - Application.getInstance().getBaseContext()).edit(); - if (count < 2) - editor.putBoolean( - Application.getInstance().getString( - R.string.contacts_show_accounts_key), false); - else - editor.putBoolean( - Application.getInstance().getString( - R.string.contacts_enable_show_accounts_key), - false); - editor.commit(); - break; - case 36: - sql = "ALTER TABLE accounts ADD COLUMN custom INTEGER;"; - DatabaseManager.execSQL(db, sql); - sql = "UPDATE accounts SET custom = 1;"; - DatabaseManager.execSQL(db, sql); - break; - case 37: - sql = "ALTER TABLE accounts ADD COLUMN sasl_enabled INTEGER;"; - DatabaseManager.execSQL(db, sql); - sql = "UPDATE accounts SET sasl_enabled = 1;"; - DatabaseManager.execSQL(db, sql); - break; - case 43: - DatabaseManager.renameTable(db, "accounts", "accounts_"); - sql = "CREATE TABLE accounts (" + "_id INTEGER PRIMARY KEY," - + "custom INTEGER," + "host TEXT," + "port INTEGER," - + "server_name TEXT," + "user_name TEXT," - + "password TEXT," + "resource TEXT," - + "color_index INTEGER," + "priority INTEGER," - + "status_mode INTEGER," + "status_text TEXT," - + "enabled INTEGER," + "sasl_enabled INTEGER," - + "required_tls INTEGER," + "compression INTEGER);"; - DatabaseManager.execSQL(db, sql); - String fields = "custom, host, port, server_name, user_name, password, " - + "resource, color_index, priority, status_mode, status_text, " - + "enabled, sasl_enabled, required_tls, compression"; - sql = "INSERT INTO accounts (" + fields + ") " + "SELECT " + fields - + " FROM accounts_;"; - DatabaseManager.execSQL(db, sql); - DatabaseManager.dropTable(db, "accounts_"); - break; - case 46: - sql = "ALTER TABLE accounts ADD COLUMN protocol TEXT;"; - DatabaseManager.execSQL(db, sql); - sql = "UPDATE accounts SET protocol = 'xmpp';"; - DatabaseManager.execSQL(db, sql); - break; - case 48: - sql = "ALTER TABLE accounts ADD COLUMN syncable INTEGER;"; - DatabaseManager.execSQL(db, sql); - sql = "UPDATE accounts SET syncable = 0;"; - DatabaseManager.execSQL(db, sql); - break; - case 50: - sql = "ALTER TABLE accounts ADD COLUMN store_password INTEGER;"; - DatabaseManager.execSQL(db, sql); - sql = "UPDATE accounts SET store_password = 1;"; - DatabaseManager.execSQL(db, sql); - break; - case 53: - sql = "UPDATE accounts SET protocol = 'gtalk' WHERE host = 'talk.google.com';"; - DatabaseManager.execSQL(db, sql); - break; - case 55: - sql = "ALTER TABLE accounts ADD COLUMN public_key BLOB;"; - DatabaseManager.execSQL(db, sql); - sql = "ALTER TABLE accounts ADD COLUMN private_key BLOB;"; - DatabaseManager.execSQL(db, sql); - break; - case 59: - sql = "ALTER TABLE accounts ADD COLUMN last_sync INTEGER;"; - DatabaseManager.execSQL(db, sql); - break; - case 61: - sql = "ALTER TABLE accounts ADD COLUMN archive_mode INTEGER;"; - DatabaseManager.execSQL(db, sql); - ArchiveMode archiveMode; - String value = PreferenceManager.getDefaultSharedPreferences( - Application.getInstance().getBaseContext()).getString( - "chats_history", "all"); - if ("all".equals(value)) - archiveMode = ArchiveMode.available; - else if ("unread".equals(value)) - archiveMode = ArchiveMode.unreadOnly; - else - archiveMode = ArchiveMode.dontStore; - sql = "UPDATE accounts SET archive_mode = " + archiveMode.ordinal() - + ";"; - DatabaseManager.execSQL(db, sql); - break; - case 66: - sql = "ALTER TABLE accounts ADD COLUMN proxy_type INTEGER;"; - DatabaseManager.execSQL(db, sql); - sql = "ALTER TABLE accounts ADD COLUMN proxy_host TEXT;"; - DatabaseManager.execSQL(db, sql); - sql = "ALTER TABLE accounts ADD COLUMN proxy_port INTEGER;"; - DatabaseManager.execSQL(db, sql); - sql = "ALTER TABLE accounts ADD COLUMN proxy_user TEXT;"; - DatabaseManager.execSQL(db, sql); - sql = "ALTER TABLE accounts ADD COLUMN proxy_password TEXT;"; - DatabaseManager.execSQL(db, sql); - sql = "UPDATE accounts SET proxy_type = " - + ProxyType.none.ordinal() + ", " - + "proxy_host = \"localhost\", " + "proxy_port = 8080, " - + "proxy_user = \"\", " + "proxy_password = \"\" " - + "WHERE proxy_type IS NULL;"; - DatabaseManager.execSQL(db, sql); - break; - default: - break; - } - } - - /** - * Adds or updates account. - * - * @return Assigned id. - */ - long write(Long id, AccountProtocol protocol, boolean custom, String host, - int port, String serverName, String userName, String resource, - boolean storePassword, String password, int colorIndex, - int priority, StatusMode statusMode, String statusText, - boolean enabled, boolean saslEnabled, TLSMode tlsMode, - boolean compression, ProxyType proxyType, String proxyHost, - int proxyPort, String proxyUser, String proxyPassword, - boolean syncable, KeyPair keyPair, Date lastSync, - ArchiveMode archiveMode) { - ContentValues values = new ContentValues(); - values.put(Fields.PROTOCOL, protocol.name()); - values.put(Fields.CUSTOM, custom ? 1 : 0); - values.put(Fields.HOST, host); - values.put(Fields.PORT, port); - values.put(Fields.SERVER_NAME, serverName); - values.put(Fields.USER_NAME, userName); - if (!storePassword) - password = AccountItem.UNDEFINED_PASSWORD; - values.put(Fields.PASSWORD, password); - values.put(Fields.RESOURCE, resource); - values.put(Fields.COLOR_INDEX, colorIndex); - values.put(Fields.PRIORITY, priority); - values.put(Fields.STATUS_MODE, statusMode.ordinal()); - values.put(Fields.STATUS_TEXT, statusText); - values.put(Fields.ENABLED, enabled ? 1 : 0); - values.put(Fields.SASL_ENABLED, saslEnabled ? 1 : 0); - values.put(Fields.TLS_MODE, tlsMode.ordinal()); - values.put(Fields.COMPRESSION, compression ? 1 : 0); - values.put(Fields.PROXY_TYPE, proxyType.ordinal()); - values.put(Fields.PROXY_HOST, proxyHost); - values.put(Fields.PROXY_PORT, proxyPort); - values.put(Fields.PROXY_USER, proxyUser); - values.put(Fields.PROXY_PASSWORD, proxyPassword); - values.put(Fields.SYNCABLE, syncable ? 1 : 0); - values.put(Fields.STORE_PASSWORD, storePassword ? 1 : 0); - if (keyPair == null) { - values.putNull(Fields.PUBLIC_KEY); - values.putNull(Fields.PRIVATE_KEY); - } else { - PublicKey publicKey = keyPair.getPublic(); - X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec( - publicKey.getEncoded()); - PrivateKey privateKey = keyPair.getPrivate(); - PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec( - privateKey.getEncoded()); - byte[] publicKeyBytes = x509EncodedKeySpec.getEncoded(); - byte[] privateKeyBytes = pkcs8EncodedKeySpec.getEncoded(); - values.put(Fields.PUBLIC_KEY, publicKeyBytes); - values.put(Fields.PRIVATE_KEY, privateKeyBytes); - } - if (lastSync == null) - values.putNull(Fields.LAST_SYNC); - else - values.put(Fields.LAST_SYNC, lastSync.getTime()); - values.put(Fields.ARCHIVE_MODE, archiveMode.ordinal()); - SQLiteDatabase db = databaseManager.getWritableDatabase(); - if (id == null) - return db.insert(NAME, Fields.USER_NAME, values); - db.update(NAME, values, Fields._ID + " = ?", - new String[] { String.valueOf(id) }); - return id; - } - - /** - * Delete record from database. - * - * @param accountItem - * @return True if record was removed. - */ - void remove(String account, long id) { - SQLiteDatabase db = databaseManager.getWritableDatabase(); - db.delete(NAME, Fields._ID + " = ?", - new String[] { String.valueOf(id) }); - databaseManager.removeAccount(account); - } - - @Override - public void clear() { - // Don't remove accounts on clear request. - } - - void wipe() { - super.clear(); - } - - @Override - protected String getTableName() { - return NAME; - } - - @Override - protected String[] getProjection() { - return PROJECTION; - } - - static long getId(Cursor cursor) { - return cursor.getLong(cursor.getColumnIndex(Fields._ID)); - } - - static AccountProtocol getProtocol(Cursor cursor) { - return AccountProtocol.valueOf(cursor.getString(cursor - .getColumnIndex(Fields.PROTOCOL))); - } - - static String getHost(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.HOST)); - } - - static boolean isCustom(Cursor cursor) { - return cursor.getInt(cursor.getColumnIndex(Fields.CUSTOM)) != 0; - } - - static int getPort(Cursor cursor) { - return cursor.getInt(cursor.getColumnIndex(Fields.PORT)); - } - - static String getServerName(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.SERVER_NAME)); - } - - static String getUserName(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.USER_NAME)); - } - - static String getPassword(Cursor cursor) { - if (!isStorePassword(cursor)) - return AccountItem.UNDEFINED_PASSWORD; - return cursor.getString(cursor.getColumnIndex(Fields.PASSWORD)); - } - - static String getResource(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.RESOURCE)); - } - - static int getColorIndex(Cursor cursor) { - return cursor.getInt(cursor.getColumnIndex(Fields.COLOR_INDEX)); - } - - static int getPriority(Cursor cursor) { - return cursor.getInt(cursor.getColumnIndex(Fields.PRIORITY)); - } - - static StatusMode getStatusMode(Cursor cursor) { - int statusModeIndex = cursor.getInt(cursor - .getColumnIndex(Fields.STATUS_MODE)); - return StatusMode.values()[statusModeIndex]; - } - - static String getStatusText(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.STATUS_TEXT)); - } - - static boolean isEnabled(Cursor cursor) { - return cursor.getInt(cursor.getColumnIndex(Fields.ENABLED)) != 0; - } - - static boolean isSaslEnabled(Cursor cursor) { - return cursor.getInt(cursor.getColumnIndex(Fields.SASL_ENABLED)) != 0; - } - - public static TLSMode getTLSMode(Cursor cursor) { - int tlsModeIndex = cursor - .getInt(cursor.getColumnIndex(Fields.TLS_MODE)); - return TLSMode.values()[tlsModeIndex]; - } - - static boolean isCompression(Cursor cursor) { - return cursor.getInt(cursor.getColumnIndex(Fields.COMPRESSION)) != 0; - } - - static boolean isSyncable(Cursor cursor) { - return cursor.getInt(cursor.getColumnIndex(Fields.SYNCABLE)) != 0; - } - - static boolean isStorePassword(Cursor cursor) { - return cursor.getInt(cursor.getColumnIndex(Fields.STORE_PASSWORD)) != 0; - } - - static Date getLastSync(Cursor cursor) { - if (cursor.isNull(cursor.getColumnIndex(Fields.LAST_SYNC))) - return null; - else - return new Date(cursor.getLong(cursor - .getColumnIndex(Fields.LAST_SYNC))); - } - - static ArchiveMode getArchiveMode(Cursor cursor) { - int index = cursor.getInt(cursor.getColumnIndex(Fields.ARCHIVE_MODE)); - return ArchiveMode.values()[index]; - } - - static ProxyType getProxyType(Cursor cursor) { - int index = cursor.getInt(cursor.getColumnIndex(Fields.PROXY_TYPE)); - return ProxyType.values()[index]; - } - - static String getProxyHost(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.PROXY_HOST)); - } - - static int getProxyPort(Cursor cursor) { - return cursor.getInt(cursor.getColumnIndex(Fields.PROXY_PORT)); - } - - static String getProxyUser(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.PROXY_USER)); - } - - static String getProxyPassword(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.PROXY_PASSWORD)); - } - - static KeyPair getKeyPair(Cursor cursor) { - byte[] publicKeyBytes = cursor.getBlob(cursor - .getColumnIndex(Fields.PUBLIC_KEY)); - byte[] privateKeyBytes = cursor.getBlob(cursor - .getColumnIndex(Fields.PRIVATE_KEY)); - if (privateKeyBytes == null || publicKeyBytes == null) - return null; - X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec( - publicKeyBytes); - PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec( - privateKeyBytes); - PublicKey publicKey; - PrivateKey privateKey; - KeyFactory keyFactory; - try { - keyFactory = KeyFactory.getInstance("DSA"); - publicKey = keyFactory.generatePublic(publicKeySpec); - privateKey = keyFactory.generatePrivate(privateKeySpec); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } catch (InvalidKeySpecException e) { - throw new RuntimeException(e); - } - return new KeyPair(publicKey, privateKey); - } + private static final String NAME = "accounts"; + private static final String[] PROJECTION = new String[]{Fields._ID, + Fields.PROTOCOL, Fields.CUSTOM, Fields.HOST, Fields.PORT, + Fields.SERVER_NAME, Fields.USER_NAME, Fields.PASSWORD, + Fields.RESOURCE, Fields.COLOR_INDEX, Fields.PRIORITY, + Fields.STATUS_MODE, Fields.STATUS_TEXT, Fields.ENABLED, + Fields.SASL_ENABLED, Fields.TLS_MODE, Fields.COMPRESSION, + Fields.SYNCABLE, Fields.STORE_PASSWORD, Fields.PUBLIC_KEY, + Fields.PRIVATE_KEY, Fields.LAST_SYNC, Fields.ARCHIVE_MODE, + Fields.PROXY_TYPE, Fields.PROXY_HOST, Fields.PROXY_PORT, + Fields.PROXY_USER, Fields.PROXY_PASSWORD}; + private final static AccountTable instance; + + static { + instance = new AccountTable(DatabaseManager.getInstance()); + DatabaseManager.getInstance().addTable(instance); + } + + private final DatabaseManager databaseManager; + + private AccountTable(DatabaseManager databaseManager) { + this.databaseManager = databaseManager; + } + + public static AccountTable getInstance() { + return instance; + } + + static long getId(Cursor cursor) { + return cursor.getLong(cursor.getColumnIndex(Fields._ID)); + } + + static AccountProtocol getProtocol(Cursor cursor) { + return AccountProtocol.valueOf(cursor.getString(cursor.getColumnIndex(Fields.PROTOCOL))); + } + + static String getHost(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.HOST)); + } + + static boolean isCustom(Cursor cursor) { + return cursor.getInt(cursor.getColumnIndex(Fields.CUSTOM)) != 0; + } + + static int getPort(Cursor cursor) { + return cursor.getInt(cursor.getColumnIndex(Fields.PORT)); + } + + static String getServerName(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.SERVER_NAME)); + } + + static String getUserName(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.USER_NAME)); + } + + static String getPassword(Cursor cursor) { + if (!isStorePassword(cursor)) { + return AccountItem.UNDEFINED_PASSWORD; + } + return cursor.getString(cursor.getColumnIndex(Fields.PASSWORD)); + } + + static String getResource(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.RESOURCE)); + } + + static int getColorIndex(Cursor cursor) { + return cursor.getInt(cursor.getColumnIndex(Fields.COLOR_INDEX)); + } + + static int getPriority(Cursor cursor) { + return cursor.getInt(cursor.getColumnIndex(Fields.PRIORITY)); + } + + static StatusMode getStatusMode(Cursor cursor) { + int statusModeIndex = cursor.getInt(cursor.getColumnIndex(Fields.STATUS_MODE)); + return StatusMode.values()[statusModeIndex]; + } + + static String getStatusText(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.STATUS_TEXT)); + } + + static boolean isEnabled(Cursor cursor) { + return cursor.getInt(cursor.getColumnIndex(Fields.ENABLED)) != 0; + } + + static boolean isSaslEnabled(Cursor cursor) { + return cursor.getInt(cursor.getColumnIndex(Fields.SASL_ENABLED)) != 0; + } + + public static TLSMode getTLSMode(Cursor cursor) { + int tlsModeIndex = cursor.getInt(cursor.getColumnIndex(Fields.TLS_MODE)); + return TLSMode.values()[tlsModeIndex]; + } + + static boolean isCompression(Cursor cursor) { + return cursor.getInt(cursor.getColumnIndex(Fields.COMPRESSION)) != 0; + } + + static boolean isSyncable(Cursor cursor) { + return cursor.getInt(cursor.getColumnIndex(Fields.SYNCABLE)) != 0; + } + + static boolean isStorePassword(Cursor cursor) { + return cursor.getInt(cursor.getColumnIndex(Fields.STORE_PASSWORD)) != 0; + } + + static Date getLastSync(Cursor cursor) { + if (cursor.isNull(cursor.getColumnIndex(Fields.LAST_SYNC))) { + return null; + } else { + return new Date(cursor.getLong(cursor.getColumnIndex(Fields.LAST_SYNC))); + } + } + + static ArchiveMode getArchiveMode(Cursor cursor) { + int index = cursor.getInt(cursor.getColumnIndex(Fields.ARCHIVE_MODE)); + return ArchiveMode.values()[index]; + } + + static ProxyType getProxyType(Cursor cursor) { + int index = cursor.getInt(cursor.getColumnIndex(Fields.PROXY_TYPE)); + return ProxyType.values()[index]; + } + + static String getProxyHost(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.PROXY_HOST)); + } + + static int getProxyPort(Cursor cursor) { + return cursor.getInt(cursor.getColumnIndex(Fields.PROXY_PORT)); + } + + static String getProxyUser(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.PROXY_USER)); + } + + static String getProxyPassword(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.PROXY_PASSWORD)); + } + + static KeyPair getKeyPair(Cursor cursor) { + byte[] publicKeyBytes = cursor.getBlob(cursor.getColumnIndex(Fields.PUBLIC_KEY)); + byte[] privateKeyBytes = cursor.getBlob(cursor.getColumnIndex(Fields.PRIVATE_KEY)); + if (privateKeyBytes == null || publicKeyBytes == null) { + return null; + } + X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyBytes); + PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes); + PublicKey publicKey; + PrivateKey privateKey; + KeyFactory keyFactory; + try { + keyFactory = KeyFactory.getInstance("DSA"); + publicKey = keyFactory.generatePublic(publicKeySpec); + privateKey = keyFactory.generatePrivate(privateKeySpec); + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + throw new RuntimeException(e); + } + return new KeyPair(publicKey, privateKey); + } + + @Override + public void create(SQLiteDatabase db) { + String sql = "CREATE TABLE " + NAME + " (" + Fields._ID + + " INTEGER PRIMARY KEY," + Fields.PROTOCOL + " TEXT," + + Fields.CUSTOM + " INTEGER," + Fields.HOST + " TEXT," + + Fields.PORT + " INTEGER," + Fields.SERVER_NAME + " TEXT," + + Fields.USER_NAME + " TEXT," + Fields.PASSWORD + " TEXT," + + Fields.RESOURCE + " TEXT," + Fields.COLOR_INDEX + " INTEGER," + + Fields.PRIORITY + " INTEGER," + Fields.STATUS_MODE + + " INTEGER," + Fields.STATUS_TEXT + " TEXT," + Fields.ENABLED + + " INTEGER," + Fields.SASL_ENABLED + " INTEGER," + + Fields.TLS_MODE + " INTEGER," + Fields.COMPRESSION + + " INTEGER," + Fields.SYNCABLE + " INTEGER," + + Fields.STORE_PASSWORD + " INTEGER," + Fields.PUBLIC_KEY + + " BLOB," + Fields.PRIVATE_KEY + " BLOB," + Fields.LAST_SYNC + + " INTEGER," + Fields.ARCHIVE_MODE + " INTEGER," + + Fields.PROXY_TYPE + " INTEGER," + Fields.PROXY_HOST + + " TEXT," + Fields.PROXY_PORT + " INTEGER," + + Fields.PROXY_USER + " TEXT," + Fields.PROXY_PASSWORD + + " TEXT);"; + DatabaseManager.execSQL(db, sql); + } + + @Override + public void migrate(SQLiteDatabase db, int toVersion) { + super.migrate(db, toVersion); + String sql; + switch (toVersion) { + case 3: + DatabaseManager.renameTable(db, "accounts", "accounts_"); + sql = "CREATE TABLE accounts (" + "_id INTEGER PRIMARY KEY," + + "host TEXT," + "port INTEGER," + "server_name TEXT," + + "user_name TEXT," + "password TEXT," + "resource TEXT," + + "color_index INTEGER," + "priority INTEGER," + + "status_mode INTEGER," + "status_text TEXT);"; + DatabaseManager.execSQL(db, sql); + sql = "INSERT INTO accounts SELECT " + + "_id, host, port, server_name, user_name, password, resource, " + + "_id, 0, " + StatusMode.available.ordinal() + + ", '' FROM accounts_;"; + DatabaseManager.execSQL(db, sql); + DatabaseManager.dropTable(db, "accounts_"); + break; + case 9: + sql = "ALTER TABLE accounts ADD COLUMN enabled INTEGER;"; + DatabaseManager.execSQL(db, sql); + sql = "UPDATE accounts SET enabled = 1;"; + DatabaseManager.execSQL(db, sql); + break; + case 21: + sql = "ALTER TABLE accounts ADD COLUMN required_tls INTEGER;"; + DatabaseManager.execSQL(db, sql); + sql = "UPDATE accounts SET required_tls = 0;"; + DatabaseManager.execSQL(db, sql); + break; + case 22: + sql = "ALTER TABLE accounts ADD COLUMN compression INTEGER;"; + DatabaseManager.execSQL(db, sql); + sql = "UPDATE accounts SET compression = 0;"; + DatabaseManager.execSQL(db, sql); + break; + case 30: + sql = "ALTER TABLE accounts ADD COLUMN share_location INTEGER;"; + DatabaseManager.execSQL(db, sql); + sql = "ALTER TABLE accounts ADD COLUMN accept_location INTEGER;"; + DatabaseManager.execSQL(db, sql); + sql = "UPDATE accounts SET share_location = 0, accept_location = 1;"; + DatabaseManager.execSQL(db, sql); + break; + case 31: + sql = "UPDATE accounts SET accept_location = 0;"; + DatabaseManager.execSQL(db, sql); + break; + case 34: + long count = db.compileStatement("SELECT COUNT(*) FROM accounts;").simpleQueryForLong(); + Editor editor = PreferenceManager.getDefaultSharedPreferences( + Application.getInstance().getBaseContext()).edit(); + if (count < 2) { + editor.putBoolean(Application.getInstance().getString( + R.string.contacts_show_accounts_key), false); + } else { + editor.putBoolean(Application.getInstance().getString( + R.string.contacts_enable_show_accounts_key), false); + } + editor.commit(); + break; + case 36: + sql = "ALTER TABLE accounts ADD COLUMN custom INTEGER;"; + DatabaseManager.execSQL(db, sql); + sql = "UPDATE accounts SET custom = 1;"; + DatabaseManager.execSQL(db, sql); + break; + case 37: + sql = "ALTER TABLE accounts ADD COLUMN sasl_enabled INTEGER;"; + DatabaseManager.execSQL(db, sql); + sql = "UPDATE accounts SET sasl_enabled = 1;"; + DatabaseManager.execSQL(db, sql); + break; + case 43: + DatabaseManager.renameTable(db, "accounts", "accounts_"); + sql = "CREATE TABLE accounts (" + "_id INTEGER PRIMARY KEY," + + "custom INTEGER," + "host TEXT," + "port INTEGER," + + "server_name TEXT," + "user_name TEXT," + + "password TEXT," + "resource TEXT," + + "color_index INTEGER," + "priority INTEGER," + + "status_mode INTEGER," + "status_text TEXT," + + "enabled INTEGER," + "sasl_enabled INTEGER," + + "required_tls INTEGER," + "compression INTEGER);"; + DatabaseManager.execSQL(db, sql); + String fields = "custom, host, port, server_name, user_name, password, " + + "resource, color_index, priority, status_mode, status_text, " + + "enabled, sasl_enabled, required_tls, compression"; + sql = "INSERT INTO accounts (" + fields + ") " + "SELECT " + fields + + " FROM accounts_;"; + DatabaseManager.execSQL(db, sql); + DatabaseManager.dropTable(db, "accounts_"); + break; + case 46: + sql = "ALTER TABLE accounts ADD COLUMN protocol TEXT;"; + DatabaseManager.execSQL(db, sql); + sql = "UPDATE accounts SET protocol = 'xmpp';"; + DatabaseManager.execSQL(db, sql); + break; + case 48: + sql = "ALTER TABLE accounts ADD COLUMN syncable INTEGER;"; + DatabaseManager.execSQL(db, sql); + sql = "UPDATE accounts SET syncable = 0;"; + DatabaseManager.execSQL(db, sql); + break; + case 50: + sql = "ALTER TABLE accounts ADD COLUMN store_password INTEGER;"; + DatabaseManager.execSQL(db, sql); + sql = "UPDATE accounts SET store_password = 1;"; + DatabaseManager.execSQL(db, sql); + break; + case 53: + sql = "UPDATE accounts SET protocol = 'gtalk' WHERE host = 'talk.google.com';"; + DatabaseManager.execSQL(db, sql); + break; + case 55: + sql = "ALTER TABLE accounts ADD COLUMN public_key BLOB;"; + DatabaseManager.execSQL(db, sql); + sql = "ALTER TABLE accounts ADD COLUMN private_key BLOB;"; + DatabaseManager.execSQL(db, sql); + break; + case 59: + sql = "ALTER TABLE accounts ADD COLUMN last_sync INTEGER;"; + DatabaseManager.execSQL(db, sql); + break; + case 61: + sql = "ALTER TABLE accounts ADD COLUMN archive_mode INTEGER;"; + DatabaseManager.execSQL(db, sql); + ArchiveMode archiveMode; + String value = PreferenceManager.getDefaultSharedPreferences( + Application.getInstance().getBaseContext()).getString( + "chats_history", "all"); + switch (value) { + case "all": + archiveMode = ArchiveMode.available; + break; + case "unread": + archiveMode = ArchiveMode.unreadOnly; + break; + default: + archiveMode = ArchiveMode.dontStore; + break; + } + sql = "UPDATE accounts SET archive_mode = " + archiveMode.ordinal() + + ";"; + DatabaseManager.execSQL(db, sql); + break; + case 66: + sql = "ALTER TABLE accounts ADD COLUMN proxy_type INTEGER;"; + DatabaseManager.execSQL(db, sql); + sql = "ALTER TABLE accounts ADD COLUMN proxy_host TEXT;"; + DatabaseManager.execSQL(db, sql); + sql = "ALTER TABLE accounts ADD COLUMN proxy_port INTEGER;"; + DatabaseManager.execSQL(db, sql); + sql = "ALTER TABLE accounts ADD COLUMN proxy_user TEXT;"; + DatabaseManager.execSQL(db, sql); + sql = "ALTER TABLE accounts ADD COLUMN proxy_password TEXT;"; + DatabaseManager.execSQL(db, sql); + sql = "UPDATE accounts SET proxy_type = " + + ProxyType.none.ordinal() + ", " + + "proxy_host = \"localhost\", " + "proxy_port = 8080, " + + "proxy_user = \"\", " + "proxy_password = \"\" " + + "WHERE proxy_type IS NULL;"; + DatabaseManager.execSQL(db, sql); + break; + default: + break; + } + } + + public long write(Long id, AccountItem accountItem) { + ConnectionSettings connectionSettings = accountItem.getConnectionSettings(); + + ContentValues values = new ContentValues(); + values.put(Fields.PROTOCOL, connectionSettings.getProtocol().name()); + values.put(Fields.CUSTOM, connectionSettings.isCustom() ? 1 : 0); + values.put(Fields.HOST, connectionSettings.getHost()); + values.put(Fields.PORT, connectionSettings.getPort()); + values.put(Fields.SERVER_NAME, connectionSettings.getServerName()); + values.put(Fields.USER_NAME, connectionSettings.getUserName()); + + String password = connectionSettings.getPassword(); + if (!accountItem.isStorePassword()) { + password = AccountItem.UNDEFINED_PASSWORD; + } + values.put(Fields.PASSWORD, password); + + values.put(Fields.RESOURCE, connectionSettings.getResource()); + values.put(Fields.COLOR_INDEX, accountItem.getColorIndex()); + values.put(Fields.PRIORITY, accountItem.getPriority()); + values.put(Fields.STATUS_MODE, accountItem.getRawStatusMode().ordinal()); + values.put(Fields.STATUS_TEXT, accountItem.getStatusText()); + values.put(Fields.ENABLED, accountItem.isEnabled() ? 1 : 0); + values.put(Fields.SASL_ENABLED, connectionSettings.isSaslEnabled() ? 1 : 0); + values.put(Fields.TLS_MODE, connectionSettings.getTlsMode().ordinal()); + values.put(Fields.COMPRESSION, connectionSettings.useCompression() ? 1 : 0); + values.put(Fields.PROXY_TYPE, connectionSettings.getProxyType().ordinal()); + values.put(Fields.PROXY_HOST, connectionSettings.getProxyHost()); + values.put(Fields.PROXY_PORT, connectionSettings.getProxyPort()); + values.put(Fields.PROXY_USER, connectionSettings.getProxyUser()); + values.put(Fields.PROXY_PASSWORD, connectionSettings.getProxyPassword()); + values.put(Fields.SYNCABLE, accountItem.isSyncable() ? 1 : 0); + values.put(Fields.STORE_PASSWORD, accountItem.isStorePassword() ? 1 : 0); + + final KeyPair keyPair = accountItem.getKeyPair(); + if (keyPair == null) { + values.putNull(Fields.PUBLIC_KEY); + values.putNull(Fields.PRIVATE_KEY); + } else { + PublicKey publicKey = keyPair.getPublic(); + X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey.getEncoded()); + PrivateKey privateKey = keyPair.getPrivate(); + PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKey.getEncoded()); + byte[] publicKeyBytes = x509EncodedKeySpec.getEncoded(); + byte[] privateKeyBytes = pkcs8EncodedKeySpec.getEncoded(); + values.put(Fields.PUBLIC_KEY, publicKeyBytes); + values.put(Fields.PRIVATE_KEY, privateKeyBytes); + } + if (accountItem.getLastSync() == null) { + values.putNull(Fields.LAST_SYNC); + } else { + values.put(Fields.LAST_SYNC, accountItem.getLastSync().getTime()); + } + values.put(Fields.ARCHIVE_MODE, accountItem.getArchiveMode().ordinal()); + SQLiteDatabase db = databaseManager.getWritableDatabase(); + if (id == null) { + return db.insert(NAME, Fields.USER_NAME, values); + } + db.update(NAME, values, Fields._ID + " = ?", new String[]{String.valueOf(id)}); + return id; + } + + /** + * Delete record from database. + * + * @param accountItem + * @return True if record was removed. + */ + void remove(String account, long id) { + SQLiteDatabase db = databaseManager.getWritableDatabase(); + db.delete(NAME, Fields._ID + " = ?", new String[]{ String.valueOf(id) }); + databaseManager.removeAccount(account); + } + + @Override + public void clear() { + // Don't remove accounts on clear request. + } + + void wipe() { + super.clear(); + } + + @Override + protected String getTableName() { + return NAME; + } + + @Override + protected String[] getProjection() { + return PROJECTION; + } + + private static final class Fields implements BaseColumns { + public static final String ENABLED = "enabled"; + public static final String SERVER_NAME = "server_name"; + public static final String USER_NAME = "user_name"; + public static final String PASSWORD = "password"; + public static final String RESOURCE = "resource"; + public static final String PRIORITY = "priority"; + public static final String STATUS_MODE = "status_mode"; + public static final String STATUS_TEXT = "status_text"; + public static final String CUSTOM = "custom"; + public static final String HOST = "host"; + public static final String PORT = "port"; + public static final String SASL_ENABLED = "sasl_enabled"; + public static final String TLS_MODE = "required_tls"; + public static final String COMPRESSION = "compression"; + public static final String COLOR_INDEX = "color_index"; + public static final String PROTOCOL = "protocol"; + public static final String SYNCABLE = "syncable"; + public static final String STORE_PASSWORD = "store_password"; + public static final String PUBLIC_KEY = "public_key"; + public static final String PRIVATE_KEY = "private_key"; + public static final String LAST_SYNC = "last_sync"; + public static final String ARCHIVE_MODE = "archive_mode"; + public static final String PROXY_TYPE = "proxy_type"; + public static final String PROXY_HOST = "proxy_host"; + public static final String PROXY_PORT = "proxy_port"; + public static final String PROXY_USER = "proxy_user"; + public static final String PROXY_PASSWORD = "proxy_password"; + + private Fields() { + } + } } diff --git a/app/src/main/java/com/xabber/android/data/account/AccountType.java b/app/src/main/java/com/xabber/android/data/account/AccountType.java index f870fae1f0..0e0be1f5bf 100644 --- a/app/src/main/java/com/xabber/android/data/account/AccountType.java +++ b/app/src/main/java/com/xabber/android/data/account/AccountType.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -21,127 +21,127 @@ /** * Account presets. - * + * * @author alexander.ivanov */ public class AccountType { - /** - * String resoure ID. - */ - final private int id; - - /** - * Protocol. - */ - final private AccountProtocol protocol; - - /** - * Name of account type. - */ - final private String name; - - /** - * Default server. - */ - final private List servers; - - /** - * Icon. - */ - final private Drawable icon; - - /** - * Hint in user field. - */ - final private String hint; - - /** - * Help text. - */ - final private String help; - - /** - * Allow to enter server name. - */ - final private boolean allowServer; - - /** - * Specific host. - */ - final private String host; - - /** - * Specific port. - */ - final private int port; - - /** - * TLS is required. - */ - final private boolean tlsRequired; - - public AccountType(int id, AccountProtocol protocol, String name, - String hint, String help, Drawable icon, boolean allowServer, - String host, int port, boolean tlsRequired, List servers) { - this.id = id; - this.protocol = protocol; - this.name = name; - this.hint = hint; - this.help = help; - this.icon = icon; - this.allowServer = allowServer; - this.servers = Collections.unmodifiableList(servers); - this.host = host; - this.port = port; - this.tlsRequired = tlsRequired; - } - - public int getId() { - return id; - } - - public AccountProtocol getProtocol() { - return protocol; - } - - public String getName() { - return name; - } - - public String getFirstServer() { - return servers.get(0); - } - - public List getServers() { - return servers; - } - - public Drawable getIcon() { - return icon; - } - - public String getHint() { - return hint; - } - - public String getHelp() { - return help; - } - - public boolean isAllowServer() { - return allowServer; - } - - public String getHost() { - return host; - } - - public int getPort() { - return port; - } - - public boolean isTLSRequired() { - return tlsRequired; - } + /** + * String resoure ID. + */ + final private int id; + + /** + * Protocol. + */ + final private AccountProtocol protocol; + + /** + * Name of account type. + */ + final private String name; + + /** + * Default server. + */ + final private List servers; + + /** + * Icon. + */ + final private Drawable icon; + + /** + * Hint in user field. + */ + final private String hint; + + /** + * Help text. + */ + final private String help; + + /** + * Allow to enter server name. + */ + final private boolean allowServer; + + /** + * Specific host. + */ + final private String host; + + /** + * Specific port. + */ + final private int port; + + /** + * TLS is required. + */ + final private boolean tlsRequired; + + public AccountType(int id, AccountProtocol protocol, String name, + String hint, String help, Drawable icon, boolean allowServer, + String host, int port, boolean tlsRequired, List servers) { + this.id = id; + this.protocol = protocol; + this.name = name; + this.hint = hint; + this.help = help; + this.icon = icon; + this.allowServer = allowServer; + this.servers = Collections.unmodifiableList(servers); + this.host = host; + this.port = port; + this.tlsRequired = tlsRequired; + } + + public int getId() { + return id; + } + + public AccountProtocol getProtocol() { + return protocol; + } + + public String getName() { + return name; + } + + public String getFirstServer() { + return servers.get(0); + } + + public List getServers() { + return servers; + } + + public Drawable getIcon() { + return icon; + } + + public String getHint() { + return hint; + } + + public String getHelp() { + return help; + } + + public boolean isAllowServer() { + return allowServer; + } + + public String getHost() { + return host; + } + + public int getPort() { + return port; + } + + public boolean isTLSRequired() { + return tlsRequired; + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/data/account/ArchiveMode.java b/app/src/main/java/com/xabber/android/data/account/ArchiveMode.java index 715bad79c6..81c4bfcd41 100644 --- a/app/src/main/java/com/xabber/android/data/account/ArchiveMode.java +++ b/app/src/main/java/com/xabber/android/data/account/ArchiveMode.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,47 +16,46 @@ /** * Message archive mode used for account. - * + * * @author alexander.ivanov - * */ public enum ArchiveMode { - /** - * Load history from server, perform replication on connection. Don't use - * local storage. - */ - server, - - /** - * If server side archive is unavailable then WORKS like {@link #local}. - * - * If server side archive becomes available and there is NO messages in - * local storage then this mode will be REPLACED with {@link #server}. - * - * If server side archive becomes available and there are messages in local - * storage then user should make a decision whether {@link #server} or - * {@link #local} should be used. - */ - available, - - /** - * Don't load history from the server. Use local storage. - */ - local, - - /** - * Don't load history from the server. Store only unread messages locally. - */ - unreadOnly, - - /** - * Don't load history from the server. Don't store messages locally. - */ - dontStore; - - public boolean saveLocally() { - return this == available || this == local; - } + /** + * Load history from server, perform replication on connection. Don't use + * local storage. + */ + server, + + /** + * If server side archive is unavailable then WORKS like {@link #local}. + *

+ * If server side archive becomes available and there is NO messages in + * local storage then this mode will be REPLACED with {@link #server}. + *

+ * If server side archive becomes available and there are messages in local + * storage then user should make a decision whether {@link #server} or + * {@link #local} should be used. + */ + available, + + /** + * Don't load history from the server. Use local storage. + */ + local, + + /** + * Don't load history from the server. Store only unread messages locally. + */ + unreadOnly, + + /** + * Don't load history from the server. Don't store messages locally. + */ + dontStore; + + public boolean saveLocally() { + return this == available || this == local; + } } diff --git a/app/src/main/java/com/xabber/android/data/account/CommonState.java b/app/src/main/java/com/xabber/android/data/account/CommonState.java index 3d04507c62..7414d39b9a 100644 --- a/app/src/main/java/com/xabber/android/data/account/CommonState.java +++ b/app/src/main/java/com/xabber/android/data/account/CommonState.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,44 +16,44 @@ /** * Common state of accounts and its connections. - * + * * @author alexander.ivanov */ public enum CommonState { - /** - * There is no one account. - */ - empty, - - /** - * There is only disabled accounts. - */ - disabled, - - /** - * There is only offline or disabled accounts. - */ - offline, - - /** - * All accounts are in waiting state or in any preceding states. - */ - waiting, - - /** - * All accounts are in connecting state or in any preceding states. - */ - connecting, - - /** - * All accounts are waiting for roster or in any preceding states. - */ - roster, - - /** - * There is at least one account with received roster. - */ - online; + /** + * There is no one account. + */ + empty, + + /** + * There is only disabled accounts. + */ + disabled, + + /** + * There is only offline or disabled accounts. + */ + offline, + + /** + * All accounts are in waiting state or in any preceding states. + */ + waiting, + + /** + * All accounts are in connecting state or in any preceding states. + */ + connecting, + + /** + * All accounts are waiting for roster or in any preceding states. + */ + roster, + + /** + * There is at least one account with received roster. + */ + online } diff --git a/app/src/main/java/com/xabber/android/data/account/OAuthManager.java b/app/src/main/java/com/xabber/android/data/account/OAuthManager.java index 7246791907..fc890e6005 100644 --- a/app/src/main/java/com/xabber/android/data/account/OAuthManager.java +++ b/app/src/main/java/com/xabber/android/data/account/OAuthManager.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -26,165 +26,164 @@ /** * Manager for OAuth authorization and account's name mapping. - * + * * @author alexander.ivanov - * */ public class OAuthManager implements OnAuthorizedListener, - OnAccountRemovedListener { - - /** - * Real full jids assigned to accounts. - */ - private final Map jids; - - /** - * Access tokens for refresh tokens. - */ - private final Map tokens; - - private final static OAuthManager instance; - - static { - instance = new OAuthManager(Application.getInstance()); - Application.getInstance().addManager(instance); - } - - public static OAuthManager getInstance() { - return instance; - } - - private OAuthManager(Application application) { - jids = new HashMap(); - tokens = new HashMap(); - } - - private OAuthProvider getOAuthProvider(AccountProtocol protocol) - throws UnsupportedOperationException { - for (OAuthProvider provider : Application.getInstance().getManagers( - OAuthProvider.class)) - if (provider.getAccountProtocol() == protocol) - return provider; - throw new UnsupportedOperationException(); - } - - /** - * Gets refresh token using Request Access Token. - * - * DON'T CALL THIS FUNTCION FROM UI THREAD. - * - * @param protocol - * @param code - * @return refresh token or null if auth failed. - * @throws NetworkException - */ - public String requestRefreshToken(AccountProtocol protocol, String code) - throws NetworkException { - return getOAuthProvider(protocol).requestRefreshToken(code); - } - - /** - * Returns password to be used in connection. - * - * @param protocol - * @param password - * @return null if password must be requested using refresh - * token. - */ - public String getPassword(AccountProtocol protocol, String password) { - if (protocol.isOAuth()) { - String accessToken = getAccessToken(password); - if (accessToken != null) - return accessToken; - return null; - } else - return password; - } - - /** - * Requests password renew from server based on refreshToken. - * - * DON'T CALL THIS FUNTCION FROM UI THREAD. - * - * @param protocol - * @param refreshToken - * @return null on authorization fail. - * @throws NetworkException - */ - public OAuthResult requestAccessToken(AccountProtocol protocol, - String refreshToken) throws NetworkException { - return getOAuthProvider(protocol).requestAccessToken(refreshToken); - } - - /** - * Update token cache. - * - * Must be call from IU thread with the value returned by - * {@link #requestAccessToken(AccountProtocol, String)}. - * - * @param oAuthResult - */ - public void onAccessTokenReceived(OAuthResult oAuthResult) { - if (oAuthResult != null) - tokens.put(oAuthResult.getRefreshToken(), oAuthResult); - } - - /** - * @param password - * @return Not expired access token or null if there is no - * access token or it was expired. - */ - private String getAccessToken(String password) { - OAuthResult result = tokens.get(password); - if (result == null) - return null; - if (!result.isExpired()) - return result.getAccessToken(); - tokens.remove(password); - return null; - } - - /** - * @param account - * @return Assigned jid or null if there is no assigned jid. - */ - public String getAssignedJid(String account) { - return jids.get(account); - } - - /** - * @param protocol - * @return Url to be opened in browser. - */ - public String getUrl(AccountProtocol protocol) { - return getOAuthProvider(protocol).getUrl(); - } - - /** - * @param protocol - * @param uri - * @return Whether redirect is valid. - */ - public boolean isValidUri(AccountProtocol protocol, Uri uri) { - return getOAuthProvider(protocol).isValidUri(uri); - } - - @Override - public void onAuthorized(ConnectionItem connection) { - if (!(connection instanceof AccountItem)) - return; - String account = ((AccountItem) connection).getAccount(); - if (!connection.getConnectionSettings().getProtocol().isOAuth()) - return; - String jid = connection.getRealJid(); - if (jid == null) - return; - jids.put(account, jid); - } - - @Override - public void onAccountRemoved(AccountItem accountItem) { - jids.remove(accountItem.getAccount()); - } + OnAccountRemovedListener { + + /** + * Real full jids assigned to accounts. + */ + private final Map jids; + + /** + * Access tokens for refresh tokens. + */ + private final Map tokens; + + private final static OAuthManager instance; + + static { + instance = new OAuthManager(Application.getInstance()); + Application.getInstance().addManager(instance); + } + + public static OAuthManager getInstance() { + return instance; + } + + private OAuthManager(Application application) { + jids = new HashMap(); + tokens = new HashMap(); + } + + private OAuthProvider getOAuthProvider(AccountProtocol protocol) + throws UnsupportedOperationException { + for (OAuthProvider provider : Application.getInstance().getManagers( + OAuthProvider.class)) + if (provider.getAccountProtocol() == protocol) + return provider; + throw new UnsupportedOperationException(); + } + + /** + * Gets refresh token using Request Access Token. + *

+ * DON'T CALL THIS FUNTCION FROM UI THREAD. + * + * @param protocol + * @param code + * @return refresh token or null if auth failed. + * @throws NetworkException + */ + public String requestRefreshToken(AccountProtocol protocol, String code) + throws NetworkException { + return getOAuthProvider(protocol).requestRefreshToken(code); + } + + /** + * Returns password to be used in connection. + * + * @param protocol + * @param password + * @return null if password must be requested using refresh + * token. + */ + public String getPassword(AccountProtocol protocol, String password) { + if (protocol.isOAuth()) { + String accessToken = getAccessToken(password); + if (accessToken != null) + return accessToken; + return null; + } else + return password; + } + + /** + * Requests password renew from server based on refreshToken. + *

+ * DON'T CALL THIS FUNTCION FROM UI THREAD. + * + * @param protocol + * @param refreshToken + * @return null on authorization fail. + * @throws NetworkException + */ + public OAuthResult requestAccessToken(AccountProtocol protocol, + String refreshToken) throws NetworkException { + return getOAuthProvider(protocol).requestAccessToken(refreshToken); + } + + /** + * Update token cache. + *

+ * Must be call from IU thread with the value returned by + * {@link #requestAccessToken(AccountProtocol, String)}. + * + * @param oAuthResult + */ + public void onAccessTokenReceived(OAuthResult oAuthResult) { + if (oAuthResult != null) + tokens.put(oAuthResult.getRefreshToken(), oAuthResult); + } + + /** + * @param password + * @return Not expired access token or null if there is no + * access token or it was expired. + */ + private String getAccessToken(String password) { + OAuthResult result = tokens.get(password); + if (result == null) + return null; + if (!result.isExpired()) + return result.getAccessToken(); + tokens.remove(password); + return null; + } + + /** + * @param account + * @return Assigned jid or null if there is no assigned jid. + */ + public String getAssignedJid(String account) { + return jids.get(account); + } + + /** + * @param protocol + * @return Url to be opened in browser. + */ + public String getUrl(AccountProtocol protocol) { + return getOAuthProvider(protocol).getUrl(); + } + + /** + * @param protocol + * @param uri + * @return Whether redirect is valid. + */ + public boolean isValidUri(AccountProtocol protocol, Uri uri) { + return getOAuthProvider(protocol).isValidUri(uri); + } + + @Override + public void onAuthorized(ConnectionItem connection) { + if (!(connection instanceof AccountItem)) + return; + String account = ((AccountItem) connection).getAccount(); + if (!connection.getConnectionSettings().getProtocol().isOAuth()) + return; + String jid = connection.getRealJid(); + if (jid == null) + return; + jids.put(account, jid); + } + + @Override + public void onAccountRemoved(AccountItem accountItem) { + jids.remove(accountItem.getAccount()); + } } diff --git a/app/src/main/java/com/xabber/android/data/account/OAuthProvider.java b/app/src/main/java/com/xabber/android/data/account/OAuthProvider.java index 878c9489b5..2af33b69af 100644 --- a/app/src/main/java/com/xabber/android/data/account/OAuthProvider.java +++ b/app/src/main/java/com/xabber/android/data/account/OAuthProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -21,49 +21,48 @@ /** * OAuth implementation. - * + * * @author alexander.ivanov - * */ interface OAuthProvider extends BaseManagerInterface { - /** - * @return Supported protocol. - */ - public AccountProtocol getAccountProtocol(); + /** + * @return Supported protocol. + */ + AccountProtocol getAccountProtocol(); - /** - * Gets refresh token using Request Access Token. - * - * DON'T CALL THIS FUNTCION FROM UI THREAD. - * - * @param code - * @return refresh token or null if auth failed. - * @throws NetworkException - */ - public String requestRefreshToken(String code) throws NetworkException; + /** + * Gets refresh token using Request Access Token. + *

+ * DON'T CALL THIS FUNTCION FROM UI THREAD. + * + * @param code + * @return refresh token or null if auth failed. + * @throws NetworkException + */ + String requestRefreshToken(String code) throws NetworkException; - /** - * Requests password renew from server based on refreshToken. - * - * DON'T CALL THIS FUNTCION FROM UI THREAD. - * - * @param refreshToken - * @return null on authorization fail. - * @throws NetworkException - */ - public OAuthResult requestAccessToken(String refreshToken) - throws NetworkException; + /** + * Requests password renew from server based on refreshToken. + *

+ * DON'T CALL THIS FUNTCION FROM UI THREAD. + * + * @param refreshToken + * @return null on authorization fail. + * @throws NetworkException + */ + OAuthResult requestAccessToken(String refreshToken) + throws NetworkException; - /** - * @return Url to be opened in browser. - */ - String getUrl(); + /** + * @return Url to be opened in browser. + */ + String getUrl(); - /** - * @param uri - * @return Whether redirect is valid. - */ - boolean isValidUri(Uri uri); + /** + * @param uri + * @return Whether redirect is valid. + */ + boolean isValidUri(Uri uri); } diff --git a/app/src/main/java/com/xabber/android/data/account/OAuthResult.java b/app/src/main/java/com/xabber/android/data/account/OAuthResult.java index fb1ed93ce2..af5fe312e0 100644 --- a/app/src/main/java/com/xabber/android/data/account/OAuthResult.java +++ b/app/src/main/java/com/xabber/android/data/account/OAuthResult.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,43 +18,42 @@ /** * Represents result of oauth response. - * + * * @author alexander.ivanov - * */ public class OAuthResult { - /** - * Number of milliseconds before actual expiration. Used to avoid token - * expiration while authorization is in progress. - */ - private static final long SHIFT = 2 * 60 * 1000; + /** + * Number of milliseconds before actual expiration. Used to avoid token + * expiration while authorization is in progress. + */ + private static final long SHIFT = 2 * 60 * 1000; - /** - * Time in milliseconds when token expires. - */ - private final long expires; + /** + * Time in milliseconds when token expires. + */ + private final long expires; - private final String accessToken; + private final String accessToken; - private final String refreshToken; + private final String refreshToken; - public OAuthResult(String accessToken, String refreshToken, long expiresIn) { - this.expires = new Date().getTime() + expiresIn - SHIFT; - this.accessToken = accessToken; - this.refreshToken = refreshToken; - } + public OAuthResult(String accessToken, String refreshToken, long expiresIn) { + this.expires = new Date().getTime() + expiresIn - SHIFT; + this.accessToken = accessToken; + this.refreshToken = refreshToken; + } - public String getAccessToken() { - return accessToken; - } + public String getAccessToken() { + return accessToken; + } - public String getRefreshToken() { - return refreshToken; - } + public String getRefreshToken() { + return refreshToken; + } - public boolean isExpired() { - return new Date().getTime() > expires; - } + public boolean isExpired() { + return new Date().getTime() > expires; + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/data/account/OnAccountAddedListener.java b/app/src/main/java/com/xabber/android/data/account/OnAccountAddedListener.java index 153227c1bd..1a6601369a 100644 --- a/app/src/main/java/com/xabber/android/data/account/OnAccountAddedListener.java +++ b/app/src/main/java/com/xabber/android/data/account/OnAccountAddedListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,11 +18,11 @@ public interface OnAccountAddedListener extends BaseManagerInterface { - /** - * New account was added to the account list. - * - * @param accountItem - */ - void onAccountAdded(AccountItem accountItem); + /** + * New account was added to the account list. + * + * @param accountItem + */ + void onAccountAdded(AccountItem accountItem); } diff --git a/app/src/main/java/com/xabber/android/data/account/OnAccountArchiveModeChangedListener.java b/app/src/main/java/com/xabber/android/data/account/OnAccountArchiveModeChangedListener.java index bb6b61ca71..e93cdf121a 100644 --- a/app/src/main/java/com/xabber/android/data/account/OnAccountArchiveModeChangedListener.java +++ b/app/src/main/java/com/xabber/android/data/account/OnAccountArchiveModeChangedListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -17,13 +17,13 @@ import com.xabber.android.data.BaseManagerInterface; public interface OnAccountArchiveModeChangedListener extends - BaseManagerInterface { + BaseManagerInterface { - /** - * Account's archive mode has been changed. - * - * @param accountItem - */ - void onAccountArchiveModeChanged(AccountItem accountItem); + /** + * Account's archive mode has been changed. + * + * @param accountItem + */ + void onAccountArchiveModeChanged(AccountItem accountItem); } diff --git a/app/src/main/java/com/xabber/android/data/account/OnAccountChangedListener.java b/app/src/main/java/com/xabber/android/data/account/OnAccountChangedListener.java index a00b60f313..2543b4e15e 100644 --- a/app/src/main/java/com/xabber/android/data/account/OnAccountChangedListener.java +++ b/app/src/main/java/com/xabber/android/data/account/OnAccountChangedListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,16 +20,15 @@ /** * Listener for any account state change. - * + * * @author alexander.ivanov - * */ public interface OnAccountChangedListener extends BaseUIListener { - /** - * State changed on connection, disconnection, authorization, etc. - * - * @param accounts - */ - public void onAccountsChanged(Collection accounts); + /** + * State changed on connection, disconnection, authorization, etc. + * + * @param accounts + */ + void onAccountsChanged(Collection accounts); } diff --git a/app/src/main/java/com/xabber/android/data/account/OnAccountDisabledListener.java b/app/src/main/java/com/xabber/android/data/account/OnAccountDisabledListener.java index 74998d521d..d06fb7abb7 100644 --- a/app/src/main/java/com/xabber/android/data/account/OnAccountDisabledListener.java +++ b/app/src/main/java/com/xabber/android/data/account/OnAccountDisabledListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,15 +20,15 @@ public interface OnAccountDisabledListener extends BaseManagerInterface { - /** - * Account was disabled. - * - * {@link OnAccountOfflineListener#onAccountOffline(AccountItem)} and - * {@link OnDisconnectListener#onDisconnect(ConnectionItem)} will be call - * first. - * - * @param accountItem - */ - void onAccountDisabled(AccountItem accountItem); + /** + * Account was disabled. + *

+ * {@link OnAccountOfflineListener#onAccountOffline(AccountItem)} and + * {@link OnDisconnectListener#onDisconnect(ConnectionItem)} will be call + * first. + * + * @param accountItem + */ + void onAccountDisabled(AccountItem accountItem); } diff --git a/app/src/main/java/com/xabber/android/data/account/OnAccountEnabledListener.java b/app/src/main/java/com/xabber/android/data/account/OnAccountEnabledListener.java index 800773defc..94ae2cc1d8 100644 --- a/app/src/main/java/com/xabber/android/data/account/OnAccountEnabledListener.java +++ b/app/src/main/java/com/xabber/android/data/account/OnAccountEnabledListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,14 +18,14 @@ public interface OnAccountEnabledListener extends BaseManagerInterface { - /** - * Account was enabled. - * - * {@link OnAccountAddedListener#onAccountAdded(AccountItem)} will be called - * first. - * - * @param accountItem - */ - void onAccountEnabled(AccountItem accountItem); + /** + * Account was enabled. + *

+ * {@link OnAccountAddedListener#onAccountAdded(AccountItem)} will be called + * first. + * + * @param accountItem + */ + void onAccountEnabled(AccountItem accountItem); } diff --git a/app/src/main/java/com/xabber/android/data/account/OnAccountOfflineListener.java b/app/src/main/java/com/xabber/android/data/account/OnAccountOfflineListener.java index 501e7d1f8a..cdb43408d8 100644 --- a/app/src/main/java/com/xabber/android/data/account/OnAccountOfflineListener.java +++ b/app/src/main/java/com/xabber/android/data/account/OnAccountOfflineListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,11 +18,11 @@ public interface OnAccountOfflineListener extends BaseManagerInterface { - /** - * Go offline requested. - * - * @param accountItem - */ - void onAccountOffline(AccountItem accountItem); + /** + * Go offline requested. + * + * @param accountItem + */ + void onAccountOffline(AccountItem accountItem); } diff --git a/app/src/main/java/com/xabber/android/data/account/OnAccountOnlineListener.java b/app/src/main/java/com/xabber/android/data/account/OnAccountOnlineListener.java index 75aa45d500..158e158e1a 100644 --- a/app/src/main/java/com/xabber/android/data/account/OnAccountOnlineListener.java +++ b/app/src/main/java/com/xabber/android/data/account/OnAccountOnlineListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,15 +20,15 @@ public interface OnAccountOnlineListener extends BaseManagerInterface { - /** - * Go online requested. - * - * {@link OnAccountEnabledListener#onAccountEnabled(AccountItem)} and - * {@link OnConnectionListener#onConnection(ConnectionItem)} will be called - * first. - * - * @param accountItem - */ - void onAccountOnline(AccountItem accountItem); + /** + * Go online requested. + *

+ * {@link OnAccountEnabledListener#onAccountEnabled(AccountItem)} and + * {@link OnConnectionListener#onConnection(ConnectionItem)} will be called + * first. + * + * @param accountItem + */ + void onAccountOnline(AccountItem accountItem); } diff --git a/app/src/main/java/com/xabber/android/data/account/OnAccountRemovedListener.java b/app/src/main/java/com/xabber/android/data/account/OnAccountRemovedListener.java index 700f244389..caf0ea20a0 100644 --- a/app/src/main/java/com/xabber/android/data/account/OnAccountRemovedListener.java +++ b/app/src/main/java/com/xabber/android/data/account/OnAccountRemovedListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,14 +18,14 @@ public interface OnAccountRemovedListener extends BaseManagerInterface { - /** - * Account was removed from account list. - * - * {@link OnAccountDisabledListener#onAccountDisabled(AccountItem)} will be - * call first. - * - * @param accountItem - */ - void onAccountRemoved(AccountItem accountItem); + /** + * Account was removed from account list. + *

+ * {@link OnAccountDisabledListener#onAccountDisabled(AccountItem)} will be + * call first. + * + * @param accountItem + */ + void onAccountRemoved(AccountItem accountItem); } diff --git a/app/src/main/java/com/xabber/android/data/account/OnAccountSyncableChangedListener.java b/app/src/main/java/com/xabber/android/data/account/OnAccountSyncableChangedListener.java index e562f5c4a7..8308174c8d 100644 --- a/app/src/main/java/com/xabber/android/data/account/OnAccountSyncableChangedListener.java +++ b/app/src/main/java/com/xabber/android/data/account/OnAccountSyncableChangedListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,11 +18,11 @@ public interface OnAccountSyncableChangedListener extends BaseManagerInterface { - /** - * Account's syncable has been changed. - * - * @param accountItem - */ - void onAccountSyncableChanged(AccountItem accountItem); + /** + * Account's syncable has been changed. + * + * @param accountItem + */ + void onAccountSyncableChanged(AccountItem accountItem); } diff --git a/app/src/main/java/com/xabber/android/data/account/PasswordRequest.java b/app/src/main/java/com/xabber/android/data/account/PasswordRequest.java index 6feb0912d0..693589f7fa 100644 --- a/app/src/main/java/com/xabber/android/data/account/PasswordRequest.java +++ b/app/src/main/java/com/xabber/android/data/account/PasswordRequest.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,32 +16,32 @@ import android.content.Intent; +import com.xabber.android.R; import com.xabber.android.data.Application; import com.xabber.android.data.entity.AccountRelated; import com.xabber.android.data.notification.AccountNotificationItem; -import com.xabber.androiddev.R; public class PasswordRequest extends AccountRelated implements - AccountNotificationItem { - - public PasswordRequest(String account) { - super(account); - } - - @Override - public Intent getIntent() { - return com.xabber.android.ui.PasswordRequest.createIntent( - Application.getInstance(), account); - } - - @Override - public String getTitle() { - return Application.getInstance().getString(R.string.PASSWORD_REQUIRED); - } - - @Override - public String getText() { - return AccountManager.getInstance().getVerboseName(account); - } + AccountNotificationItem { + + public PasswordRequest(String account) { + super(account); + } + + @Override + public Intent getIntent() { + return com.xabber.android.ui.PasswordRequest.createIntent( + Application.getInstance(), account); + } + + @Override + public String getTitle() { + return Application.getInstance().getString(R.string.PASSWORD_REQUIRED); + } + + @Override + public String getText() { + return AccountManager.getInstance().getVerboseName(account); + } } diff --git a/app/src/main/java/com/xabber/android/data/account/SavedStatus.java b/app/src/main/java/com/xabber/android/data/account/SavedStatus.java index 336b434cb3..7cd158ea8c 100644 --- a/app/src/main/java/com/xabber/android/data/account/SavedStatus.java +++ b/app/src/main/java/com/xabber/android/data/account/SavedStatus.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -17,59 +17,59 @@ public class SavedStatus implements Comparable { - private final StatusMode statusMode; - private final String statusText; + private final StatusMode statusMode; + private final String statusText; - public SavedStatus(StatusMode statusMode, String statusText) { - super(); - this.statusMode = statusMode; - this.statusText = statusText; - } + public SavedStatus(StatusMode statusMode, String statusText) { + super(); + this.statusMode = statusMode; + this.statusText = statusText; + } - public StatusMode getStatusMode() { - return statusMode; - } + public StatusMode getStatusMode() { + return statusMode; + } - public String getStatusText() { - return statusText; - } + public String getStatusText() { + return statusText; + } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result - + ((statusMode == null) ? 0 : statusMode.hashCode()); - result = prime * result - + ((statusText == null) ? 0 : statusText.hashCode()); - return result; - } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((statusMode == null) ? 0 : statusMode.hashCode()); + result = prime * result + + ((statusText == null) ? 0 : statusText.hashCode()); + return result; + } - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - SavedStatus other = (SavedStatus) obj; - if (statusMode != other.statusMode) - return false; - if (statusText == null) { - if (other.statusText != null) - return false; - } else if (!statusText.equals(other.statusText)) - return false; - return true; - } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + SavedStatus other = (SavedStatus) obj; + if (statusMode != other.statusMode) + return false; + if (statusText == null) { + if (other.statusText != null) + return false; + } else if (!statusText.equals(other.statusText)) + return false; + return true; + } - @Override - public int compareTo(SavedStatus another) { - int result = statusMode.compareTo(another.statusMode); - if (result != 0) - return result; - return statusText.compareToIgnoreCase(another.statusText); - } + @Override + public int compareTo(SavedStatus another) { + int result = statusMode.compareTo(another.statusMode); + if (result != 0) + return result; + return statusText.compareToIgnoreCase(another.statusText); + } } diff --git a/app/src/main/java/com/xabber/android/data/account/ScreenManager.java b/app/src/main/java/com/xabber/android/data/account/ScreenManager.java index 23267b7c2b..c10daf0022 100644 --- a/app/src/main/java/com/xabber/android/data/account/ScreenManager.java +++ b/app/src/main/java/com/xabber/android/data/account/ScreenManager.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -33,78 +33,77 @@ /** * Manage screen on / off. - * + * * @author alexander.ivanov - * */ public class ScreenManager implements OnInitializedListener, OnCloseListener { - private final ScreenReceiver screenReceiver; - private final AlarmManager alarmManager; - private final PendingIntent goAwayPendingIntent; - private final PendingIntent goXaPendingIntent; + private final ScreenReceiver screenReceiver; + private final AlarmManager alarmManager; + private final PendingIntent goAwayPendingIntent; + private final PendingIntent goXaPendingIntent; - private final static ScreenManager instance; + private final static ScreenManager instance; - static { - instance = new ScreenManager(); - Application.getInstance().addManager(instance); - } + static { + instance = new ScreenManager(); + Application.getInstance().addManager(instance); + } - public static ScreenManager getInstance() { - return instance; - } + public static ScreenManager getInstance() { + return instance; + } - private ScreenManager() { - screenReceiver = new ScreenReceiver(); - goAwayPendingIntent = PendingIntent.getBroadcast( - Application.getInstance(), 0, - GoAwayReceiver.createIntent(Application.getInstance()), 0); - goXaPendingIntent = PendingIntent.getBroadcast( - Application.getInstance(), 0, - GoXaReceiver.createIntent(Application.getInstance()), 0); - alarmManager = (AlarmManager) Application.getInstance() - .getSystemService(Context.ALARM_SERVICE); - } + private ScreenManager() { + screenReceiver = new ScreenReceiver(); + goAwayPendingIntent = PendingIntent.getBroadcast( + Application.getInstance(), 0, + GoAwayReceiver.createIntent(Application.getInstance()), 0); + goXaPendingIntent = PendingIntent.getBroadcast( + Application.getInstance(), 0, + GoXaReceiver.createIntent(Application.getInstance()), 0); + alarmManager = (AlarmManager) Application.getInstance() + .getSystemService(Context.ALARM_SERVICE); + } - @Override - public void onInitialized() { - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_SCREEN_ON); - filter.addAction(Intent.ACTION_SCREEN_OFF); - Application.getInstance().registerReceiver(screenReceiver, filter); - } + @Override + public void onInitialized() { + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_SCREEN_ON); + filter.addAction(Intent.ACTION_SCREEN_OFF); + Application.getInstance().registerReceiver(screenReceiver, filter); + } - @Override - public void onClose() { - alarmManager.cancel(goAwayPendingIntent); - alarmManager.cancel(goXaPendingIntent); - Application.getInstance().unregisterReceiver(screenReceiver); - } + @Override + public void onClose() { + alarmManager.cancel(goAwayPendingIntent); + alarmManager.cancel(goXaPendingIntent); + Application.getInstance().unregisterReceiver(screenReceiver); + } - private long getTime(int milliSeconds) { - Calendar calendar = Calendar.getInstance(); - calendar.setTimeInMillis(System.currentTimeMillis()); - calendar.add(Calendar.MILLISECOND, milliSeconds); - return calendar.getTimeInMillis(); - } + private long getTime(int milliSeconds) { + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(System.currentTimeMillis()); + calendar.add(Calendar.MILLISECOND, milliSeconds); + return calendar.getTimeInMillis(); + } - public void onScreen(Intent intent) { - int goAway = SettingsManager.connectionGoAway(); - int goXa = SettingsManager.connectionGoXa(); - if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) { - ConnectionManager.getInstance().updateConnections(false); - alarmManager.cancel(goAwayPendingIntent); - alarmManager.cancel(goXaPendingIntent); - AccountManager.getInstance().wakeUp(); - } else if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { - if (goAway >= 0) - alarmManager.set(AlarmManager.RTC_WAKEUP, getTime(goAway), - goAwayPendingIntent); - if (goXa >= 0) - alarmManager.set(AlarmManager.RTC_WAKEUP, getTime(goXa), - goXaPendingIntent); - } - } + public void onScreen(Intent intent) { + int goAway = SettingsManager.connectionGoAway(); + int goXa = SettingsManager.connectionGoXa(); + if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) { + ConnectionManager.getInstance().updateConnections(false); + alarmManager.cancel(goAwayPendingIntent); + alarmManager.cancel(goXaPendingIntent); + AccountManager.getInstance().wakeUp(); + } else if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { + if (goAway >= 0) + alarmManager.set(AlarmManager.RTC_WAKEUP, getTime(goAway), + goAwayPendingIntent); + if (goXa >= 0) + alarmManager.set(AlarmManager.RTC_WAKEUP, getTime(goXa), + goXaPendingIntent); + } + } } diff --git a/app/src/main/java/com/xabber/android/data/account/StatusMode.java b/app/src/main/java/com/xabber/android/data/account/StatusMode.java index 811a8da7b4..4625287076 100644 --- a/app/src/main/java/com/xabber/android/data/account/StatusMode.java +++ b/app/src/main/java/com/xabber/android/data/account/StatusMode.java @@ -1,155 +1,154 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data.account; +import com.xabber.android.R; + import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smack.packet.Presence.Mode; -import com.xabber.androiddev.R; - /** * Status mode. - * + * * @author alexander.ivanov - * */ public enum StatusMode { - /** - * Free to chat. - */ - chat, - - /** - * Available. - */ - available, - - /** - * Away. - */ - away, - - /** - * Away for an extended period of time. - */ - xa, - - /** - * Do not disturb. - */ - dnd, - - /** - * Account is invisible. - */ - invisible, - - /** - * The user is unavailable. - */ - unavailable, - - /** - * Account is in connection state. - * - * Don't use it for {@link AccountManager#setStatus(String, StatusMode)}. - */ - connection, - - /** - * Account is unable to get presence information. Account is unsubscribed. - * - * Don't use it for {@link AccountManager#setStatus(String, StatusMode)}. - */ - unsubscribed; - - /** - * Creates new {@link StatusMode} form {@link Presence}. - * - * @param mode - * @return - */ - static public StatusMode createStatusMode(Presence presence) { - if (presence.getType() == Presence.Type.unavailable) - return StatusMode.unavailable; - final Mode mode = presence.getMode(); - if (mode == Mode.away) - return StatusMode.away; - else if (mode == Mode.chat) - return StatusMode.chat; - else if (mode == Mode.dnd) - return StatusMode.dnd; - else if (mode == Mode.xa) - return StatusMode.xa; - else - return StatusMode.available; - } - - /** - * Get {@link Mode} for {@link StatusMode}. - * - * @return - */ - public Mode getMode() { - if (this == StatusMode.away) - return Mode.away; - else if (this == StatusMode.chat) - return Mode.chat; - else if (this == StatusMode.dnd) - return Mode.dnd; - else if (this == StatusMode.xa) - return Mode.xa; - else if (this == StatusMode.available) - return Mode.available; - throw new IllegalStateException(); - } - - /** - * @return ID of the string resource. - */ - public int getStringID() { - if (this == StatusMode.available) - return R.string.available; - else if (this == StatusMode.dnd) - return R.string.dnd; - else if (this == StatusMode.xa) - return R.string.xa; - else if (this == StatusMode.chat) - return R.string.chat; - else if (this == StatusMode.away) - return R.string.away; - else if (this == StatusMode.unsubscribed) - return R.string.unsubscribed; - else if (this == StatusMode.invisible) - return R.string.invisible; - return R.string.unavailable; - } - - /** - * @return Drawable level for status icon. - */ - public int getStatusLevel() { - return ordinal(); - } - - /** - * @return Whether entity is online. - */ - public boolean isOnline() { - return this != StatusMode.unavailable && this != unsubscribed; - } + /** + * Free to chat. + */ + chat, + + /** + * Available. + */ + available, + + /** + * Away. + */ + away, + + /** + * Away for an extended period of time. + */ + xa, + + /** + * Do not disturb. + */ + dnd, + + /** + * Account is invisible. + */ + invisible, + + /** + * The user is unavailable. + */ + unavailable, + + /** + * Account is in connection state. + *

+ * Don't use it for {@link AccountManager#setStatus(String, StatusMode)}. + */ + connection, + + /** + * Account is unable to get presence information. Account is unsubscribed. + *

+ * Don't use it for {@link AccountManager#setStatus(String, StatusMode)}. + */ + unsubscribed; + + /** + * Creates new {@link StatusMode} form {@link Presence}. + * + * @param mode + * @return + */ + static public StatusMode createStatusMode(Presence presence) { + if (presence.getType() == Presence.Type.unavailable) + return StatusMode.unavailable; + final Mode mode = presence.getMode(); + if (mode == Mode.away) + return StatusMode.away; + else if (mode == Mode.chat) + return StatusMode.chat; + else if (mode == Mode.dnd) + return StatusMode.dnd; + else if (mode == Mode.xa) + return StatusMode.xa; + else + return StatusMode.available; + } + + /** + * Get {@link Mode} for {@link StatusMode}. + * + * @return + */ + public Mode getMode() { + if (this == StatusMode.away) + return Mode.away; + else if (this == StatusMode.chat) + return Mode.chat; + else if (this == StatusMode.dnd) + return Mode.dnd; + else if (this == StatusMode.xa) + return Mode.xa; + else if (this == StatusMode.available) + return Mode.available; + throw new IllegalStateException(); + } + + /** + * @return ID of the string resource. + */ + public int getStringID() { + if (this == StatusMode.available) + return R.string.available; + else if (this == StatusMode.dnd) + return R.string.dnd; + else if (this == StatusMode.xa) + return R.string.xa; + else if (this == StatusMode.chat) + return R.string.chat; + else if (this == StatusMode.away) + return R.string.away; + else if (this == StatusMode.unsubscribed) + return R.string.unsubscribed; + else if (this == StatusMode.invisible) + return R.string.invisible; + return R.string.unavailable; + } + + /** + * @return Drawable level for status icon. + */ + public int getStatusLevel() { + return ordinal(); + } + + /** + * @return Whether entity is online. + */ + public boolean isOnline() { + return this != StatusMode.unavailable && this != unsubscribed; + } } diff --git a/app/src/main/java/com/xabber/android/data/account/StatusTable.java b/app/src/main/java/com/xabber/android/data/account/StatusTable.java index 5a685b9872..fda125a8ec 100644 --- a/app/src/main/java/com/xabber/android/data/account/StatusTable.java +++ b/app/src/main/java/com/xabber/android/data/account/StatusTable.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -24,139 +24,137 @@ /** * Storage with preset statuses. - * + * * @author alexander.ivanov */ class StatusTable extends AbstractTable { - private static final class Fields implements BaseColumns { - private Fields() { - } - - public static final String STATUS_MODE = "status_mode"; - public static final String STATUS_TEXT = "status_text"; - } - - private static final String NAME = "statuses"; - private static final String[] PROJECTION = new String[] { - Fields.STATUS_MODE, Fields.STATUS_TEXT }; - - private final DatabaseManager databaseManager; - private SQLiteStatement writeStatement; - private final Object writeLock; - - private final static StatusTable instance; - - static { - instance = new StatusTable(DatabaseManager.getInstance()); - DatabaseManager.getInstance().addTable(instance); - } - - public static StatusTable getInstance() { - return instance; - } - - private StatusTable(DatabaseManager databaseManager) { - this.databaseManager = databaseManager; - writeStatement = null; - writeLock = new Object(); - } - - @Override - public void create(SQLiteDatabase db) { - String sql; - sql = "CREATE TABLE " + NAME + " (" + Fields.STATUS_MODE + " INTEGER," - + Fields.STATUS_TEXT + " TEXT);"; - DatabaseManager.execSQL(db, sql); - sql = "CREATE UNIQUE INDEX " + NAME + "_index ON " + NAME + " (" - + Fields.STATUS_MODE + ", " + Fields.STATUS_TEXT + ");"; - DatabaseManager.execSQL(db, sql); - } - - @Override - public void migrate(SQLiteDatabase db, int toVersion) { - super.migrate(db, toVersion); - String sql; - switch (toVersion) { - case 12: - sql = "CREATE TABLE statuses (" + "status_mode INTEGER," - + "status_text TEXT);"; - DatabaseManager.execSQL(db, sql); - sql = "CREATE UNIQUE INDEX statuses_index ON statuses " - + "(status_mode, status_text);"; - DatabaseManager.execSQL(db, sql); - break; - case 35: - sql = "ALTER TABLE statuses ADD COLUMN share_location INTEGER;"; - DatabaseManager.execSQL(db, sql); - sql = "UPDATE statuses SET share_location = 0;"; - DatabaseManager.execSQL(db, sql); - sql = "DROP INDEX statuses_index;"; - DatabaseManager.execSQL(db, sql); - sql = "CREATE UNIQUE INDEX statuses_index ON statuses " - + "(status_mode, status_text, share_location);"; - DatabaseManager.execSQL(db, sql); - break; - case 38: - sql = "DROP INDEX statuses_index;"; - DatabaseManager.execSQL(db, sql); - DatabaseManager.renameTable(db, "statuses", "old_statuses"); - sql = "CREATE TABLE statuses (" + "status_mode INTEGER," - + "status_text TEXT);"; - DatabaseManager.execSQL(db, sql); - sql = "CREATE UNIQUE INDEX statuses_index ON statuses " - + "(status_mode, status_text);"; - DatabaseManager.execSQL(db, sql); - sql = "INSERT OR REPLACE INTO statuses (status_mode, status_text) " - + "SELECT status_mode, status_text FROM old_statuses;"; - DatabaseManager.execSQL(db, sql); - DatabaseManager.dropTable(db, "old_statuses"); - break; - default: - break; - } - } - - void write(StatusMode statusMode, String statusText) { - if (statusText == null) - statusText = ""; - synchronized (writeLock) { - if (writeStatement == null) { - SQLiteDatabase db = databaseManager.getWritableDatabase(); - writeStatement = db.compileStatement("INSERT OR REPLACE INTO " - + NAME + " (" + Fields.STATUS_MODE + ", " - + Fields.STATUS_TEXT + ") VALUES (?, ?);"); - } - writeStatement.bindLong(1, statusMode.ordinal()); - writeStatement.bindString(2, statusText); - writeStatement.execute(); - } - } - - void remove(StatusMode statusMode, String statusText) { - SQLiteDatabase db = databaseManager.getWritableDatabase(); - db.delete(NAME, Fields.STATUS_MODE + " = ? AND " + Fields.STATUS_TEXT - + " = ?", new String[] { String.valueOf(statusMode.ordinal()), - statusText }); - } - - @Override - protected String getTableName() { - return NAME; - } - - @Override - protected String[] getProjection() { - return PROJECTION; - } - - static StatusMode getStatusMode(Cursor cursor) { - return StatusMode.values()[cursor.getInt(cursor - .getColumnIndex(Fields.STATUS_MODE))]; - } - - static String getStatusText(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.STATUS_TEXT)); - } + private static final class Fields implements BaseColumns { + private Fields() { + } + + public static final String STATUS_MODE = "status_mode"; + public static final String STATUS_TEXT = "status_text"; + } + + private static final String NAME = "statuses"; + private static final String[] PROJECTION = new String[]{ + Fields.STATUS_MODE, Fields.STATUS_TEXT}; + + private final DatabaseManager databaseManager; + private SQLiteStatement writeStatement; + private final Object writeLock; + + private final static StatusTable instance; + + static { + instance = new StatusTable(DatabaseManager.getInstance()); + DatabaseManager.getInstance().addTable(instance); + } + + public static StatusTable getInstance() { + return instance; + } + + private StatusTable(DatabaseManager databaseManager) { + this.databaseManager = databaseManager; + writeStatement = null; + writeLock = new Object(); + } + + @Override + public void create(SQLiteDatabase db) { + String sql; + sql = "CREATE TABLE " + NAME + " (" + Fields.STATUS_MODE + " INTEGER," + + Fields.STATUS_TEXT + " TEXT);"; + DatabaseManager.execSQL(db, sql); + sql = "CREATE UNIQUE INDEX " + NAME + "_index ON " + NAME + " (" + + Fields.STATUS_MODE + ", " + Fields.STATUS_TEXT + ");"; + DatabaseManager.execSQL(db, sql); + } + + @Override + public void migrate(SQLiteDatabase db, int toVersion) { + super.migrate(db, toVersion); + String sql; + switch (toVersion) { + case 12: + sql = "CREATE TABLE statuses (" + "status_mode INTEGER," + + "status_text TEXT);"; + DatabaseManager.execSQL(db, sql); + sql = "CREATE UNIQUE INDEX statuses_index ON statuses " + + "(status_mode, status_text);"; + DatabaseManager.execSQL(db, sql); + break; + case 35: + sql = "ALTER TABLE statuses ADD COLUMN share_location INTEGER;"; + DatabaseManager.execSQL(db, sql); + sql = "UPDATE statuses SET share_location = 0;"; + DatabaseManager.execSQL(db, sql); + sql = "DROP INDEX statuses_index;"; + DatabaseManager.execSQL(db, sql); + sql = "CREATE UNIQUE INDEX statuses_index ON statuses " + + "(status_mode, status_text, share_location);"; + DatabaseManager.execSQL(db, sql); + break; + case 38: + sql = "DROP INDEX statuses_index;"; + DatabaseManager.execSQL(db, sql); + DatabaseManager.renameTable(db, "statuses", "old_statuses"); + sql = "CREATE TABLE statuses (" + "status_mode INTEGER," + + "status_text TEXT);"; + DatabaseManager.execSQL(db, sql); + sql = "CREATE UNIQUE INDEX statuses_index ON statuses " + + "(status_mode, status_text);"; + DatabaseManager.execSQL(db, sql); + sql = "INSERT OR REPLACE INTO statuses (status_mode, status_text) " + + "SELECT status_mode, status_text FROM old_statuses;"; + DatabaseManager.execSQL(db, sql); + DatabaseManager.dropTable(db, "old_statuses"); + break; + default: + break; + } + } + + void write(StatusMode statusMode, String statusText) { + synchronized (writeLock) { + if (writeStatement == null) { + SQLiteDatabase db = databaseManager.getWritableDatabase(); + writeStatement = db.compileStatement("INSERT OR REPLACE INTO " + + NAME + " (" + Fields.STATUS_MODE + ", " + + Fields.STATUS_TEXT + ") VALUES (?, ?);"); + } + writeStatement.bindLong(1, statusMode.ordinal()); + writeStatement.bindString(2, statusText); + writeStatement.execute(); + } + } + + void remove(StatusMode statusMode, String statusText) { + SQLiteDatabase db = databaseManager.getWritableDatabase(); + db.delete(NAME, Fields.STATUS_MODE + " = ? AND " + Fields.STATUS_TEXT + + " = ?", new String[]{String.valueOf(statusMode.ordinal()), + statusText}); + } + + @Override + protected String getTableName() { + return NAME; + } + + @Override + protected String[] getProjection() { + return PROJECTION; + } + + static StatusMode getStatusMode(Cursor cursor) { + return StatusMode.values()[cursor.getInt(cursor + .getColumnIndex(Fields.STATUS_MODE))]; + } + + static String getStatusText(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.STATUS_TEXT)); + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/data/account/WLMManager.java b/app/src/main/java/com/xabber/android/data/account/WLMManager.java index eb91c6a07f..73ef598877 100644 --- a/app/src/main/java/com/xabber/android/data/account/WLMManager.java +++ b/app/src/main/java/com/xabber/android/data/account/WLMManager.java @@ -1,23 +1,25 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data.account; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.List; +import android.net.Uri; + +import com.xabber.android.R; +import com.xabber.android.data.Application; +import com.xabber.android.data.LogManager; +import com.xabber.android.data.NetworkException; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; @@ -35,174 +37,171 @@ import org.json.JSONObject; import org.json.JSONTokener; -import android.net.Uri; - -import com.xabber.android.data.Application; -import com.xabber.android.data.LogManager; -import com.xabber.android.data.NetworkException; -import com.xabber.androiddev.R; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.List; class WLMManager implements OAuthProvider { - private static enum GrantType { - - authorizationCode("authorization_code", "code"), - - refreshToken("refresh_token", "refresh_token"); - - public final String name; - public final String value; - - private GrantType(String name, String value) { - this.name = name; - this.value = value; - } - - } - - private static final String WLM_CLIENT_SECRET = "XEazfSKu0Iu2pt6Z64Lqm-1cRxtEYgS0"; - private static final String WLM_CLIENT_ID = "00000000440923FF"; - private static final String WLM_SCOPE = "wl.messenger wl.offline_access"; - private static final String WLM_SCHEME = "https"; - private static final String WLM_AUTHORITY = "oauth.live.com"; - private static final String WLM_REDIRECT_PATH = "/desktop"; - private static final String WLM_REDIRECT_URL = new Uri.Builder() - .scheme(WLM_SCHEME).authority(WLM_AUTHORITY) - .path(WLM_REDIRECT_PATH).build().toString(); - - private final static WLMManager instance; - - static { - instance = new WLMManager(); - Application.getInstance().addManager(instance); - } - - public static WLMManager getInstance() { - return instance; - } - - private WLMManager() { - } - - /** - * @param protocol - * @param grantType - * @param value - * @return Access and refresh tokens or null if auth failed. - * @throws NetworkException - */ - private OAuthResult accessTokenOperation(GrantType grantType, String value) - throws NetworkException { - HttpPost httpPost = new HttpPost(new Uri.Builder().scheme(WLM_SCHEME) - .authority(WLM_AUTHORITY).path("token").build().toString()); - List nameValuePairs = new ArrayList(); - nameValuePairs - .add(new BasicNameValuePair("grant_type", grantType.name)); - nameValuePairs.add(new BasicNameValuePair(grantType.value, value)); - nameValuePairs.add(new BasicNameValuePair("redirect_uri", - WLM_REDIRECT_URL)); - nameValuePairs.add(new BasicNameValuePair("client_id", WLM_CLIENT_ID)); - nameValuePairs.add(new BasicNameValuePair("client_secret", - WLM_CLIENT_SECRET)); - UrlEncodedFormEntity encodedFormEntity; - try { - encodedFormEntity = new UrlEncodedFormEntity(nameValuePairs, - HTTP.UTF_8); - } catch (UnsupportedEncodingException e) { - throw new NetworkException(R.string.CONNECTION_FAILED, e); - } - String content; - try { - content = EntityUtils.toString(encodedFormEntity); - } catch (ParseException e) { - throw new NetworkException(R.string.CONNECTION_FAILED, e); - } catch (IOException e) { - throw new NetworkException(R.string.CONNECTION_FAILED, e); - } - LogManager.i(this, httpPost.getURI().toString() + "\n" + content); - httpPost.setEntity(encodedFormEntity); - HttpClient httpClient = new DefaultHttpClient(); - HttpResponse httpResponse; - try { - httpResponse = httpClient.execute(httpPost); - } catch (ClientProtocolException e) { - throw new NetworkException(R.string.CONNECTION_FAILED, e); - } catch (IOException e) { - throw new NetworkException(R.string.CONNECTION_FAILED, e); - } - HttpEntity entity = httpResponse.getEntity(); - try { - content = EntityUtils.toString(entity); - } catch (ParseException e) { - throw new NetworkException(R.string.CONNECTION_FAILED, e); - } catch (IOException e) { - throw new NetworkException(R.string.CONNECTION_FAILED, e); - } finally { - try { - entity.consumeContent(); - } catch (IOException e) { - throw new NetworkException(R.string.CONNECTION_FAILED, e); - } - } - LogManager.i(this, content); - long expiresIn; - String accessToken; - String refreshToken; - try { - JSONObject jsonObject = (JSONObject) new JSONTokener(content) - .nextValue(); - if (jsonObject.has("error")) - return null; - try { - expiresIn = Long.valueOf(jsonObject.getString("expires_in")) * 1000; - } catch (NumberFormatException e) { - throw new NetworkException(R.string.CONNECTION_FAILED, e); - } - accessToken = jsonObject.getString("access_token"); - refreshToken = jsonObject.getString("refresh_token"); - } catch (JSONException e) { - throw new NetworkException(R.string.CONNECTION_FAILED, e); - } - return new OAuthResult(accessToken, refreshToken, expiresIn); - } - - @Override - public AccountProtocol getAccountProtocol() { - return AccountProtocol.wlm; - } - - @Override - public String requestRefreshToken(String code) throws NetworkException { - OAuthResult result = accessTokenOperation(GrantType.authorizationCode, - code); - if (result == null) - return null; - else - return result.getRefreshToken(); - } - - @Override - public OAuthResult requestAccessToken(String refreshToken) - throws NetworkException { - return accessTokenOperation(GrantType.refreshToken, refreshToken); - } - - @Override - public String getUrl() { - return new Uri.Builder().scheme(WLM_SCHEME).authority(WLM_AUTHORITY) - .path("authorize") - .appendQueryParameter("response_type", "code") - .appendQueryParameter("client_id", WLM_CLIENT_ID) - .appendQueryParameter("redirect_uri", WLM_REDIRECT_URL) - .appendQueryParameter("scope", WLM_SCOPE) - .appendQueryParameter("display", "touch").build().toString(); - } - - @Override - public boolean isValidUri(Uri uri) { - return WLM_SCHEME.equals(uri.getScheme()) - && WLM_AUTHORITY.equals(uri.getAuthority()) - && WLM_REDIRECT_PATH.equals(uri.getPath()); - } + private static final String WLM_CLIENT_SECRET = "XEazfSKu0Iu2pt6Z64Lqm-1cRxtEYgS0"; + private static final String WLM_CLIENT_ID = "00000000440923FF"; + private static final String WLM_SCOPE = "wl.messenger wl.offline_access"; + private static final String WLM_SCHEME = "https"; + private static final String WLM_AUTHORITY = "oauth.live.com"; + private static final String WLM_REDIRECT_PATH = "/desktop"; + private static final String WLM_REDIRECT_URL = new Uri.Builder() + .scheme(WLM_SCHEME).authority(WLM_AUTHORITY) + .path(WLM_REDIRECT_PATH).build().toString(); + private final static WLMManager instance; + + static { + instance = new WLMManager(); + Application.getInstance().addManager(instance); + } + + private WLMManager() { + } + + public static WLMManager getInstance() { + return instance; + } + + /** + * @param protocol + * @param grantType + * @param value + * @return Access and refresh tokens or null if auth failed. + * @throws NetworkException + */ + private OAuthResult accessTokenOperation(GrantType grantType, String value) + throws NetworkException { + HttpPost httpPost = new HttpPost(new Uri.Builder().scheme(WLM_SCHEME) + .authority(WLM_AUTHORITY).path("token").build().toString()); + List nameValuePairs = new ArrayList(); + nameValuePairs + .add(new BasicNameValuePair("grant_type", grantType.name)); + nameValuePairs.add(new BasicNameValuePair(grantType.value, value)); + nameValuePairs.add(new BasicNameValuePair("redirect_uri", + WLM_REDIRECT_URL)); + nameValuePairs.add(new BasicNameValuePair("client_id", WLM_CLIENT_ID)); + nameValuePairs.add(new BasicNameValuePair("client_secret", + WLM_CLIENT_SECRET)); + UrlEncodedFormEntity encodedFormEntity; + try { + encodedFormEntity = new UrlEncodedFormEntity(nameValuePairs, + HTTP.UTF_8); + } catch (UnsupportedEncodingException e) { + throw new NetworkException(R.string.CONNECTION_FAILED, e); + } + String content; + try { + content = EntityUtils.toString(encodedFormEntity); + } catch (ParseException e) { + throw new NetworkException(R.string.CONNECTION_FAILED, e); + } catch (IOException e) { + throw new NetworkException(R.string.CONNECTION_FAILED, e); + } + LogManager.i(this, httpPost.getURI().toString() + "\n" + content); + httpPost.setEntity(encodedFormEntity); + HttpClient httpClient = new DefaultHttpClient(); + HttpResponse httpResponse; + try { + httpResponse = httpClient.execute(httpPost); + } catch (ClientProtocolException e) { + throw new NetworkException(R.string.CONNECTION_FAILED, e); + } catch (IOException e) { + throw new NetworkException(R.string.CONNECTION_FAILED, e); + } + HttpEntity entity = httpResponse.getEntity(); + try { + content = EntityUtils.toString(entity); + } catch (ParseException e) { + throw new NetworkException(R.string.CONNECTION_FAILED, e); + } catch (IOException e) { + throw new NetworkException(R.string.CONNECTION_FAILED, e); + } finally { + try { + entity.consumeContent(); + } catch (IOException e) { + throw new NetworkException(R.string.CONNECTION_FAILED, e); + } + } + LogManager.i(this, content); + long expiresIn; + String accessToken; + String refreshToken; + try { + JSONObject jsonObject = (JSONObject) new JSONTokener(content) + .nextValue(); + if (jsonObject.has("error")) + return null; + try { + expiresIn = Long.valueOf(jsonObject.getString("expires_in")) * 1000; + } catch (NumberFormatException e) { + throw new NetworkException(R.string.CONNECTION_FAILED, e); + } + accessToken = jsonObject.getString("access_token"); + refreshToken = jsonObject.getString("refresh_token"); + } catch (JSONException e) { + throw new NetworkException(R.string.CONNECTION_FAILED, e); + } + return new OAuthResult(accessToken, refreshToken, expiresIn); + } + + @Override + public AccountProtocol getAccountProtocol() { + return AccountProtocol.wlm; + } + + @Override + public String requestRefreshToken(String code) throws NetworkException { + OAuthResult result = accessTokenOperation(GrantType.authorizationCode, + code); + if (result == null) + return null; + else + return result.getRefreshToken(); + } + + @Override + public OAuthResult requestAccessToken(String refreshToken) + throws NetworkException { + return accessTokenOperation(GrantType.refreshToken, refreshToken); + } + + @Override + public String getUrl() { + return new Uri.Builder().scheme(WLM_SCHEME).authority(WLM_AUTHORITY) + .path("authorize") + .appendQueryParameter("response_type", "code") + .appendQueryParameter("client_id", WLM_CLIENT_ID) + .appendQueryParameter("redirect_uri", WLM_REDIRECT_URL) + .appendQueryParameter("scope", WLM_SCOPE) + .appendQueryParameter("display", "touch").build().toString(); + } + + @Override + public boolean isValidUri(Uri uri) { + return WLM_SCHEME.equals(uri.getScheme()) + && WLM_AUTHORITY.equals(uri.getAuthority()) + && WLM_REDIRECT_PATH.equals(uri.getPath()); + } + + private enum GrantType { + + authorizationCode("authorization_code", "code"), + + refreshToken("refresh_token", "refresh_token"); + + public final String name; + public final String value; + + GrantType(String name, String value) { + this.name = name; + this.value = value; + } + + } } diff --git a/app/src/main/java/com/xabber/android/data/connection/AbstractPool.java b/app/src/main/java/com/xabber/android/data/connection/AbstractPool.java index 6db1d8fe1e..f659b7c879 100644 --- a/app/src/main/java/com/xabber/android/data/connection/AbstractPool.java +++ b/app/src/main/java/com/xabber/android/data/connection/AbstractPool.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -21,66 +21,64 @@ /** * Address pool. - * + * * @author alexander.ivanov - * */ public abstract class AbstractPool { - protected final Queue pool; - protected final Queue used; + protected final Queue pool; + protected final Queue used; - public AbstractPool() { - this.pool = new LinkedList(); - this.used = new LinkedList(); - } + public AbstractPool() { + this.pool = new LinkedList(); + this.used = new LinkedList(); + } - /** - * Updates pool with specified values. - * - * @param values - * null if no changes in the pool should be made. - */ - synchronized public void update(Source[] values) { - if (values == null) - return; - List items = new ArrayList(); - for (Source value : values) { - Item item = convert(value); - if (item != null) - items.add(item); - } - update(items); - } + /** + * Updates pool with specified values. + * + * @param values null if no changes in the pool should be made. + */ + synchronized public void update(Source[] values) { + if (values == null) + return; + List items = new ArrayList(); + for (Source value : values) { + Item item = convert(value); + if (item != null) + items.add(item); + } + update(items); + } - /** - * @param value - * @return Converted value or null if value shouldn't be used. - */ - abstract Item convert(Source value); + /** + * @param value + * @return Converted value or null if value shouldn't be used. + */ + abstract Item convert(Source value); - /** - * Checks and updates pool with items. - * - * @param items - */ - abstract void update(List items); + /** + * Checks and updates pool with items. + * + * @param items + */ + abstract void update(List items); - /** - * Returns first value from the pool. Make it as used. Reset pool if it is - * empty. - * - * @return null if pool was empty. - */ - synchronized public Item getNext() { - if (pool.isEmpty()) { - pool.addAll(used); - used.clear(); - return null; - } - Item address = pool.remove(); - used.add(address); - return address; - } + /** + * Returns first value from the pool. Make it as used. Reset pool if it is + * empty. + * + * @return null if pool was empty. + */ + synchronized public Item getNext() { + if (pool.isEmpty()) { + pool.addAll(used); + used.clear(); + return null; + } + Item address = pool.remove(); + used.add(address); + return address; + } } diff --git a/app/src/main/java/com/xabber/android/data/connection/CertificateInvalidReason.java b/app/src/main/java/com/xabber/android/data/connection/CertificateInvalidReason.java index 2b5f722980..73578857a9 100644 --- a/app/src/main/java/com/xabber/android/data/connection/CertificateInvalidReason.java +++ b/app/src/main/java/com/xabber/android/data/connection/CertificateInvalidReason.java @@ -1,53 +1,52 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data.connection; -import com.xabber.androiddev.R; +import com.xabber.android.R; /** * Possible reasons for certificate invalidation. - * + * * @author alexander.ivanov - * */ public enum CertificateInvalidReason { - /** - * Signature, subject, issuer or date verification failed. - */ - invalidChane, + /** + * Signature, subject, issuer or date verification failed. + */ + invalidChane, - /** - * Self-signed certificate. - */ - selfSigned, + /** + * Self-signed certificate. + */ + selfSigned, - /** - * Target verification failed. - */ - invalidTarget; + /** + * Target verification failed. + */ + invalidTarget; - public int getResourceId() { - if (this == invalidChane) - return R.string.certificate_invalid_chane; - else if (this == selfSigned) - return R.string.certificate_self_signed; - else if (this == invalidTarget) - return R.string.certificate_invalid_target; - else - throw new IllegalStateException(); - } + public int getResourceId() { + if (this == invalidChane) + return R.string.certificate_invalid_chane; + else if (this == selfSigned) + return R.string.certificate_self_signed; + else if (this == invalidTarget) + return R.string.certificate_invalid_target; + else + throw new IllegalStateException(); + } } diff --git a/app/src/main/java/com/xabber/android/data/connection/CertificateManager.java b/app/src/main/java/com/xabber/android/data/connection/CertificateManager.java index 28a48c1d73..3263b99d0a 100644 --- a/app/src/main/java/com/xabber/android/data/connection/CertificateManager.java +++ b/app/src/main/java/com/xabber/android/data/connection/CertificateManager.java @@ -1,19 +1,32 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data.connection; +import android.content.res.AssetManager; + +import com.xabber.android.R; +import com.xabber.android.data.Application; +import com.xabber.android.data.LogManager; +import com.xabber.android.data.OnClearListener; +import com.xabber.android.data.OnLoadListener; +import com.xabber.android.data.notification.BaseNotificationProvider; +import com.xabber.android.data.notification.NotificationManager; + +import org.jivesoftware.smack.CertificateListener; +import org.jivesoftware.smack.util.StringUtils; + import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -34,431 +47,412 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import org.jivesoftware.smack.CertificateListener; -import org.jivesoftware.smack.util.StringUtils; - -import android.content.res.AssetManager; - -import com.xabber.android.data.Application; -import com.xabber.android.data.LogManager; -import com.xabber.android.data.OnClearListener; -import com.xabber.android.data.OnLoadListener; -import com.xabber.android.data.notification.BaseNotificationProvider; -import com.xabber.android.data.notification.NotificationManager; -import com.xabber.androiddev.R; - /** * Manage certificate exceptions. - * + *

* Key store in ".bsk" asset will be used as default * allowed certificates. - * + * * @author alexander.ivanov - * */ public class CertificateManager implements OnLoadListener, OnClearListener { - private static final String INVALID = "invalid"; - - private static final char[] PASSWORD = "password".toCharArray(); - - /** - * File to be used to store user certificates. - */ - private static final Map KEY_FILES; - - private static final CertificateManager instance; - - static { - instance = new CertificateManager(); - Application.getInstance().addManager(instance); - - KEY_FILES = new HashMap(); - for (CertificateInvalidReason reason : CertificateInvalidReason - .values()) - KEY_FILES.put(reason, new File(Application.getInstance() - .getFilesDir(), reason.toString() + ".bsk")); - } - - public static CertificateManager getInstance() { - return instance; - } - - /** - * Key store for confirmed certificates. - */ - private final Map keyStores; - - /** - * Key store for preset certificates. - */ - private final Map defaultStores; - - private final BaseNotificationProvider pendingCertificateProvider = new BaseNotificationProvider( - R.drawable.ic_stat_auth_failed) { - - @Override - public void clearNotifications() { - ignoreCertificates.addAll(getNotifications()); - super.clearNotifications(); - } - - }; - - /** - * Certificate issues not to be displayed to the user. - */ - private final Collection ignoreCertificates; - - private CertificateManager() { - defaultStores = new HashMap(); - keyStores = new ConcurrentHashMap(); - ignoreCertificates = new ArrayList(); - } - - @Override - public void onLoad() { - final Map defaultStores = new HashMap(); - final Map keyStores = new HashMap(); - AssetManager assetManager = Application.getInstance().getResources() - .getAssets(); - for (CertificateInvalidReason reason : CertificateInvalidReason - .values()) { - InputStream stream; - try { - stream = assetManager.open(reason.toString() + ".bsk"); - } catch (IOException e) { - stream = null; - } - defaultStores.put(reason, loadKeyStore(stream)); - stream = getInputStream(KEY_FILES.get(reason)); - if (stream != null) - keyStores.put(reason, loadKeyStore(stream)); - } - Application.getInstance().runOnUiThread(new Runnable() { - @Override - public void run() { - onLoaded(defaultStores, keyStores); - } - }); - } - - private static InputStream getInputStream(File file) { - if (file.exists()) { - try { - return new FileInputStream(file); - } catch (FileNotFoundException e) { - throw new RuntimeException(e); - } - } else { - return null; - } - } - - private static KeyStore loadKeyStore(InputStream stream) { - KeyStore keyStore; - try { - keyStore = KeyStore.getInstance("BKS"); - } catch (KeyStoreException e) { - throw new RuntimeException(e); - } - try { - keyStore.load(stream, PASSWORD); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } catch (IOException e) { - throw new RuntimeException(e); - } catch (CertificateException e) { - throw new RuntimeException(e); - } - try { - if (stream != null) - stream.close(); - } catch (IOException e) { - throw new RuntimeException(e); - } - return keyStore; - } - - /** - * Create new key store instance based on default key store. - * - * @param reason - * @return - * @throws KeyStoreException - */ - private KeyStore createKeyStore(CertificateInvalidReason reason) { - KeyStore source = defaultStores.get(reason); - Enumeration enumeration; - try { - enumeration = source.aliases(); - } catch (KeyStoreException e) { - throw new RuntimeException(e); - } - KeyStore keyStore = loadKeyStore(null); - while (enumeration.hasMoreElements()) { - String alias = enumeration.nextElement(); - try { - keyStore.setCertificateEntry(alias, - source.getCertificate(alias)); - } catch (KeyStoreException e) { - throw new RuntimeException(e); - } - } - return keyStore; - } - - private void onLoaded( - Map defaultStores, - Map keyStores) { - this.defaultStores.putAll(defaultStores); - this.keyStores.putAll(keyStores); - for (CertificateInvalidReason reason : CertificateInvalidReason - .values()) - if (!this.keyStores.containsKey(reason)) - this.keyStores.put(reason, createKeyStore(reason)); - NotificationManager.getInstance().registerNotificationProvider( - pendingCertificateProvider); - } - - /** - * Verify whether this certificate was previously allowed by user. And - * create pending notification to accept or decline it. - * - * @param server - * @param x509Certificate - * invalid certificate. - * @param reason - * reason of invalidation. - * @return whether this certificate was allowed by user. - */ - private boolean isTrustedCertificate(final String server, - final X509Certificate x509Certificate, - final CertificateInvalidReason reason) { - KeyStore keyStore = keyStores.get(reason); - try { - if (keyStore != null - && keyStore.getCertificateAlias(x509Certificate) != null) - return true; - } catch (KeyStoreException e) { - LogManager.exception(this, e); - } - final String fingerprint = getFingerprint(x509Certificate); - Application.getInstance().runOnUiThread(new Runnable() { - @Override - public void run() { - if (getPendingCertificate(fingerprint, reason) != null - || getPendingCertificate(fingerprint, reason, - ignoreCertificates) != null) - return; - pendingCertificateProvider.add(new PendingCertificate(server, - reason, x509Certificate, fingerprint), true); - } - }); - return false; - } - - /** - * @param fingerprint - * @param reason - * @param collection - * @return Pending certificate or null. - */ - private static PendingCertificate getPendingCertificate(String fingerprint, - CertificateInvalidReason reason, - Collection collection) { - for (PendingCertificate pendingCertificate : collection) - if (pendingCertificate.getFingerprint().equals(fingerprint) - && reason == pendingCertificate.getReason()) - return pendingCertificate; - return null; - } - - public PendingCertificate getPendingCertificate(String fingerprint, - CertificateInvalidReason reason) { - return getPendingCertificate(fingerprint, reason, - pendingCertificateProvider.getNotifications()); - } - - /** - * Accept pending certificate. - * - * @param fingerprint - * @param reason - */ - public void accept(String fingerprint, final CertificateInvalidReason reason) { - PendingCertificate pendingCertificate = getPendingCertificate( - fingerprint, reason); - if (pendingCertificate == null) - return; - String alias; - while (true) { - alias = StringUtils.randomString(8); - try { - if (!keyStores.get(reason).containsAlias(alias)) - break; - } catch (KeyStoreException e) { - LogManager.exception(this, e); - return; - } - } - try { - keyStores.get(reason).setEntry( - alias, - new KeyStore.TrustedCertificateEntry(pendingCertificate - .getX509Certificate()), null); - } catch (KeyStoreException e) { - LogManager.exception(this, e); - return; - } - pendingCertificateProvider.remove(pendingCertificate); - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - FileOutputStream out; - try { - out = new FileOutputStream(KEY_FILES.get(reason)); - } catch (FileNotFoundException e) { - LogManager.exception(CertificateManager.this, e); - return; - } - try { - keyStores.get(reason).store(out, PASSWORD); - } catch (KeyStoreException e) { - LogManager.exception(CertificateManager.this, e); - } catch (NoSuchAlgorithmException e) { - LogManager.exception(CertificateManager.this, e); - } catch (IOException e) { - LogManager.exception(CertificateManager.this, e); - } catch (CertificateException e) { - LogManager.exception(CertificateManager.this, e); - } - try { - out.close(); - } catch (IOException e) { - LogManager.exception(CertificateManager.this, e); - } - } - }); - } - - /** - * Ignore pending certificate. - * - * @param fingerprint - * @param reason - */ - public void discard(String fingerprint, CertificateInvalidReason reason) { - PendingCertificate pendingCertificate = getPendingCertificate( - fingerprint, reason); - if (pendingCertificate == null) - return; - pendingCertificateProvider.remove(pendingCertificate); - ignoreCertificates.add(pendingCertificate); - } - - /** - * @param x509Certificate - * @return Finger print for the given certificate. - */ - private static String getFingerprint(X509Certificate x509Certificate) { - byte[] data; - try { - data = x509Certificate.getEncoded(); - } catch (CertificateEncodingException e) { - LogManager.exception(PendingCertificate.class, e); - return INVALID; - } - MessageDigest digest; - try { - digest = MessageDigest.getInstance("SHA-1"); - } catch (NoSuchAlgorithmException e) { - LogManager.exception(PendingCertificate.class, e); - return INVALID; - } - digest.update(data); - byte[] bytes = digest.digest(); - return StringUtils.encodeHex(bytes); - } - - /** - * @param fingerprint - * @return Formatted fingerprint to be shown. - */ - public static String showFingerprint(String fingerprint) { - if (fingerprint == null) - return null; - StringBuffer buffer = new StringBuffer(); - for (int index = 0; index < fingerprint.length(); index++) { - if (index > 0 && index % 2 == 0) - buffer.append(':'); - buffer.append(fingerprint.charAt(index)); - } - return buffer.toString().toUpperCase(); - } - - @Override - public void onClear() { - for (File file : KEY_FILES.values()) - file.delete(); - } - - /** - * Removes all certificates. - */ - public void removeCertificates() { - pendingCertificateProvider.clearNotifications(); - ignoreCertificates.clear(); - for (CertificateInvalidReason reason : CertificateInvalidReason - .values()) - keyStores.put(reason, createKeyStore(reason)); - Application.getInstance().runInBackground(new Runnable() { - - @Override - public void run() { - for (File file : KEY_FILES.values()) - file.delete(); - } - - }); - } - - public CertificateListener createCertificateListener( - ConnectionItem connectionItem) { - final String server = connectionItem.getConnectionSettings() - .getServerName(); - return new CertificateListener() { - - @Override - public boolean onValid(X509Certificate[] chain) { - return true; - } - - @Override - public boolean onSelfSigned(X509Certificate certificate, - CertificateException exception) { - LogManager.exception(CertificateManager.this, exception); - return isTrustedCertificate(server, certificate, - CertificateInvalidReason.selfSigned); - } - - @Override - public boolean onInvalidTarget(X509Certificate certificate, - CertificateException exception) { - LogManager.exception(CertificateManager.this, exception); - return isTrustedCertificate(server, certificate, - CertificateInvalidReason.invalidTarget); - } - - @Override - public boolean onInvalidChain(X509Certificate[] chain, - CertificateException exception) { - LogManager.exception(CertificateManager.this, exception); - return isTrustedCertificate(server, chain[0], - CertificateInvalidReason.invalidChane); - } - - }; - } + private static final String INVALID = "invalid"; + + private static final char[] PASSWORD = "password".toCharArray(); + + /** + * File to be used to store user certificates. + */ + private static final Map KEY_FILES; + + private static final CertificateManager instance; + + static { + instance = new CertificateManager(); + Application.getInstance().addManager(instance); + + KEY_FILES = new HashMap(); + for (CertificateInvalidReason reason : CertificateInvalidReason + .values()) + KEY_FILES.put(reason, new File(Application.getInstance() + .getFilesDir(), reason.toString() + ".bsk")); + } + + /** + * Key store for confirmed certificates. + */ + private final Map keyStores; + /** + * Key store for preset certificates. + */ + private final Map defaultStores; + /** + * Certificate issues not to be displayed to the user. + */ + private final Collection ignoreCertificates; + private final BaseNotificationProvider pendingCertificateProvider = new BaseNotificationProvider( + R.drawable.ic_stat_error) { + + @Override + public void clearNotifications() { + ignoreCertificates.addAll(getNotifications()); + super.clearNotifications(); + } + + }; + + private CertificateManager() { + defaultStores = new HashMap(); + keyStores = new ConcurrentHashMap(); + ignoreCertificates = new ArrayList(); + } + + public static CertificateManager getInstance() { + return instance; + } + + private static InputStream getInputStream(File file) { + if (file.exists()) { + try { + return new FileInputStream(file); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + } else { + return null; + } + } + + private static KeyStore loadKeyStore(InputStream stream) { + KeyStore keyStore; + try { + keyStore = KeyStore.getInstance("BKS"); + } catch (KeyStoreException e) { + throw new RuntimeException(e); + } + try { + keyStore.load(stream, PASSWORD); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (CertificateException e) { + throw new RuntimeException(e); + } + try { + if (stream != null) + stream.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return keyStore; + } + + /** + * @param fingerprint + * @param reason + * @param collection + * @return Pending certificate or null. + */ + private static PendingCertificate getPendingCertificate(String fingerprint, + CertificateInvalidReason reason, + Collection collection) { + for (PendingCertificate pendingCertificate : collection) + if (pendingCertificate.getFingerprint().equals(fingerprint) + && reason == pendingCertificate.getReason()) + return pendingCertificate; + return null; + } + + /** + * @param x509Certificate + * @return Finger print for the given certificate. + */ + private static String getFingerprint(X509Certificate x509Certificate) { + byte[] data; + try { + data = x509Certificate.getEncoded(); + } catch (CertificateEncodingException e) { + LogManager.exception(PendingCertificate.class, e); + return INVALID; + } + MessageDigest digest; + try { + digest = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException e) { + LogManager.exception(PendingCertificate.class, e); + return INVALID; + } + digest.update(data); + byte[] bytes = digest.digest(); + return StringUtils.encodeHex(bytes); + } + + /** + * @param fingerprint + * @return Formatted fingerprint to be shown. + */ + public static String showFingerprint(String fingerprint) { + if (fingerprint == null) + return null; + StringBuffer buffer = new StringBuffer(); + for (int index = 0; index < fingerprint.length(); index++) { + if (index > 0 && index % 2 == 0) + buffer.append(':'); + buffer.append(fingerprint.charAt(index)); + } + return buffer.toString().toUpperCase(); + } + + @Override + public void onLoad() { + final Map defaultStores = new HashMap(); + final Map keyStores = new HashMap(); + AssetManager assetManager = Application.getInstance().getResources() + .getAssets(); + for (CertificateInvalidReason reason : CertificateInvalidReason + .values()) { + InputStream stream; + try { + stream = assetManager.open(reason.toString() + ".bsk"); + } catch (IOException e) { + stream = null; + } + defaultStores.put(reason, loadKeyStore(stream)); + stream = getInputStream(KEY_FILES.get(reason)); + if (stream != null) + keyStores.put(reason, loadKeyStore(stream)); + } + Application.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + onLoaded(defaultStores, keyStores); + } + }); + } + + /** + * Create new key store instance based on default key store. + * + * @param reason + * @return + * @throws KeyStoreException + */ + private KeyStore createKeyStore(CertificateInvalidReason reason) { + KeyStore source = defaultStores.get(reason); + Enumeration enumeration; + try { + enumeration = source.aliases(); + } catch (KeyStoreException e) { + throw new RuntimeException(e); + } + KeyStore keyStore = loadKeyStore(null); + while (enumeration.hasMoreElements()) { + String alias = enumeration.nextElement(); + try { + keyStore.setCertificateEntry(alias, + source.getCertificate(alias)); + } catch (KeyStoreException e) { + throw new RuntimeException(e); + } + } + return keyStore; + } + + private void onLoaded( + Map defaultStores, + Map keyStores) { + this.defaultStores.putAll(defaultStores); + this.keyStores.putAll(keyStores); + for (CertificateInvalidReason reason : CertificateInvalidReason + .values()) + if (!this.keyStores.containsKey(reason)) + this.keyStores.put(reason, createKeyStore(reason)); + NotificationManager.getInstance().registerNotificationProvider( + pendingCertificateProvider); + } + + /** + * Verify whether this certificate was previously allowed by user. And + * create pending notification to accept or decline it. + * + * @param server + * @param x509Certificate invalid certificate. + * @param reason reason of invalidation. + * @return whether this certificate was allowed by user. + */ + private boolean isTrustedCertificate(final String server, + final X509Certificate x509Certificate, + final CertificateInvalidReason reason) { + KeyStore keyStore = keyStores.get(reason); + try { + if (keyStore != null + && keyStore.getCertificateAlias(x509Certificate) != null) + return true; + } catch (KeyStoreException e) { + LogManager.exception(this, e); + } + final String fingerprint = getFingerprint(x509Certificate); + Application.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + if (getPendingCertificate(fingerprint, reason) != null + || getPendingCertificate(fingerprint, reason, + ignoreCertificates) != null) + return; + pendingCertificateProvider.add(new PendingCertificate(server, + reason, x509Certificate, fingerprint), true); + } + }); + return false; + } + + public PendingCertificate getPendingCertificate(String fingerprint, + CertificateInvalidReason reason) { + return getPendingCertificate(fingerprint, reason, + pendingCertificateProvider.getNotifications()); + } + + /** + * Accept pending certificate. + * + * @param fingerprint + * @param reason + */ + public void accept(String fingerprint, final CertificateInvalidReason reason) { + PendingCertificate pendingCertificate = getPendingCertificate( + fingerprint, reason); + if (pendingCertificate == null) + return; + String alias; + while (true) { + alias = StringUtils.randomString(8); + try { + if (!keyStores.get(reason).containsAlias(alias)) + break; + } catch (KeyStoreException e) { + LogManager.exception(this, e); + return; + } + } + try { + keyStores.get(reason).setEntry( + alias, + new KeyStore.TrustedCertificateEntry(pendingCertificate + .getX509Certificate()), null); + } catch (KeyStoreException e) { + LogManager.exception(this, e); + return; + } + pendingCertificateProvider.remove(pendingCertificate); + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + FileOutputStream out; + try { + out = new FileOutputStream(KEY_FILES.get(reason)); + } catch (FileNotFoundException e) { + LogManager.exception(CertificateManager.this, e); + return; + } + try { + keyStores.get(reason).store(out, PASSWORD); + } catch (KeyStoreException e) { + LogManager.exception(CertificateManager.this, e); + } catch (NoSuchAlgorithmException e) { + LogManager.exception(CertificateManager.this, e); + } catch (IOException e) { + LogManager.exception(CertificateManager.this, e); + } catch (CertificateException e) { + LogManager.exception(CertificateManager.this, e); + } + try { + out.close(); + } catch (IOException e) { + LogManager.exception(CertificateManager.this, e); + } + } + }); + } + + /** + * Ignore pending certificate. + * + * @param fingerprint + * @param reason + */ + public void discard(String fingerprint, CertificateInvalidReason reason) { + PendingCertificate pendingCertificate = getPendingCertificate( + fingerprint, reason); + if (pendingCertificate == null) + return; + pendingCertificateProvider.remove(pendingCertificate); + ignoreCertificates.add(pendingCertificate); + } + + @Override + public void onClear() { + for (File file : KEY_FILES.values()) + file.delete(); + } + + /** + * Removes all certificates. + */ + public void removeCertificates() { + pendingCertificateProvider.clearNotifications(); + ignoreCertificates.clear(); + for (CertificateInvalidReason reason : CertificateInvalidReason + .values()) + keyStores.put(reason, createKeyStore(reason)); + Application.getInstance().runInBackground(new Runnable() { + + @Override + public void run() { + for (File file : KEY_FILES.values()) + file.delete(); + } + + }); + } + + public CertificateListener createCertificateListener( + ConnectionItem connectionItem) { + final String server = connectionItem.getConnectionSettings() + .getServerName(); + return new CertificateListener() { + + @Override + public boolean onValid(X509Certificate[] chain) { + return true; + } + + @Override + public boolean onSelfSigned(X509Certificate certificate, + CertificateException exception) { + LogManager.exception(CertificateManager.this, exception); + return isTrustedCertificate(server, certificate, + CertificateInvalidReason.selfSigned); + } + + @Override + public boolean onInvalidTarget(X509Certificate certificate, + CertificateException exception) { + LogManager.exception(CertificateManager.this, exception); + return isTrustedCertificate(server, certificate, + CertificateInvalidReason.invalidTarget); + } + + @Override + public boolean onInvalidChain(X509Certificate[] chain, + CertificateException exception) { + LogManager.exception(CertificateManager.this, exception); + return isTrustedCertificate(server, chain[0], + CertificateInvalidReason.invalidChane); + } + + }; + } } diff --git a/app/src/main/java/com/xabber/android/data/connection/ConnectionItem.java b/app/src/main/java/com/xabber/android/data/connection/ConnectionItem.java index 4fbf4008b1..aa539c9d95 100644 --- a/app/src/main/java/com/xabber/android/data/connection/ConnectionItem.java +++ b/app/src/main/java/com/xabber/android/data/connection/ConnectionItem.java @@ -1,310 +1,338 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data.connection; -import org.jivesoftware.smack.XMPPConnection; - +import com.xabber.android.R; import com.xabber.android.data.Application; import com.xabber.android.data.LogManager; import com.xabber.android.data.account.AccountProtocol; -import com.xabber.androiddev.R; + +import org.jivesoftware.smack.XMPPConnection; /** * Abstract connection. - * + * * @author alexander.ivanov - * */ public abstract class ConnectionItem { - /** - * Connection options. - */ - private final ConnectionSettings connectionSettings; - - /** - * XMPP connection. - */ - private ConnectionThread connectionThread; - - /** - * Connection was requested by user. - */ - private boolean connectionRequest; - - /** - * Current state. - */ - private ConnectionState state; - - /** - * Whether force reconnection is in progress. - */ - private boolean disconnectionRequested; - - public ConnectionItem(AccountProtocol protocol, boolean custom, - String host, int port, String serverName, String userName, - String resource, boolean storePassword, String password, - boolean saslEnabled, TLSMode tlsMode, boolean compression, - ProxyType proxyType, String proxyHost, int proxyPort, - String proxyUser, String proxyPassword) { - connectionSettings = new ConnectionSettings(protocol, userName, - serverName, resource, custom, host, port, password, - saslEnabled, tlsMode, compression, proxyType, proxyHost, - proxyPort, proxyUser, proxyPassword); - connectionRequest = false; - disconnectionRequested = false; - connectionThread = null; - state = ConnectionState.offline; - } - - /** - * Gets current connection thread. - * - * @return null if thread doesn't exists. - */ - public ConnectionThread getConnectionThread() { - return connectionThread; - } - - /** - * @return connection options. - */ - public ConnectionSettings getConnectionSettings() { - return connectionSettings; - } - - public ConnectionState getState() { - return state; - } - - /** - * Returns real full jid, that was assigned while login. - * - * @return null if connection is not established. - */ - public String getRealJid() { - ConnectionThread connectionThread = getConnectionThread(); - if (connectionThread == null) - return null; - XMPPConnection xmppConnection = connectionThread.getXMPPConnection(); - if (xmppConnection == null) - return null; - String user = xmppConnection.getUser(); - if (user == null) - return null; - return user; - } - - /** - * @param userRequest - * action was requested by user. - * @return Whether connection is available. - */ - protected boolean isConnectionAvailable(boolean userRequest) { - return true; - } - - /** - * Connect or disconnect from server depending on internal flags. - * - * @param userRequest - * action was requested by user. - * @return Whether state has been changed. - */ - public boolean updateConnection(boolean userRequest) { - boolean available = isConnectionAvailable(userRequest); - if (NetworkManager.getInstance().getState() != NetworkState.available - || !available || disconnectionRequested) { - ConnectionState target = available ? ConnectionState.waiting - : ConnectionState.offline; - if (state == ConnectionState.connected - || state == ConnectionState.authentication - || state == ConnectionState.connecting) { - if (userRequest) - connectionRequest = false; - if (connectionThread != null) { - disconnect(connectionThread); - // Force remove managed connection thread. - onClose(connectionThread); - connectionThread = null; - } - } else if (state == target) { - return false; - } - state = target; - return true; - } else { - if (state == ConnectionState.offline - || state == ConnectionState.waiting) { - if (userRequest) - connectionRequest = true; - state = ConnectionState.connecting; - connectionThread = new ConnectionThread(this); - if (connectionSettings.isCustom()) - connectionThread.start(connectionSettings.getHost(), - connectionSettings.getPort(), false); - else - connectionThread.start(connectionSettings.getServerName(), - 5222, true); - return true; - } else { - return false; - } - } - } - - /** - * Disconnect and connect using new connection. - */ - public void forceReconnect() { - if (!getState().isConnectable()) - return; - disconnectionRequested = true; - boolean request = connectionRequest; - connectionRequest = false; - updateConnection(false); - connectionRequest = request; - disconnectionRequested = false; - updateConnection(false); - } - - /** - * Starts disconnection in another thread. - */ - protected void disconnect(final ConnectionThread connectionThread) { - Thread thread = new Thread("Disconnection thread for " + this) { - @Override - public void run() { - XMPPConnection xmppConnection = connectionThread - .getXMPPConnection(); - if (xmppConnection != null) - try { - xmppConnection.disconnect(); - } catch (RuntimeException e) { - // connectionClose() in smack can fail. - } - }; - }; - thread.setPriority(Thread.MIN_PRIORITY); - thread.setDaemon(true); - thread.start(); - } - - /** - * @param connectionThread - * @return Whether thread is managed by connection. - */ - boolean isManaged(ConnectionThread connectionThread) { - return connectionThread == this.connectionThread; - } - - /** - * Update password. - * - * @param password - */ - protected void onPasswordChanged(String password) { - connectionSettings.setPassword(password); - } - - /** - * SRV record has been resolved. - */ - protected void onSRVResolved(ConnectionThread connectionThread) { - } - - /** - * Invalid certificate has been received. - */ - protected void onInvalidCertificate() { - } - - /** - * Connection has been established. - */ - protected void onConnected(ConnectionThread connectionThread) { - if (isManaged(connectionThread)) - state = ConnectionState.authentication; - } - - /** - * Authorization failed. - */ - protected void onAuthFailed() { - } - - /** - * Authorization passed. - */ - protected void onAuthorized(ConnectionThread connectionThread) { - if (isManaged(connectionThread)) - state = ConnectionState.connected; - } - - /** - * Called when disconnect should occur. - * - * @param connectionThread - * @return true if connection thread was managed. - */ - private boolean onDisconnect(ConnectionThread connectionThread) { - XMPPConnection xmppConnection = connectionThread.getXMPPConnection(); - boolean acceptable = isManaged(connectionThread); - if (xmppConnection == null) - LogManager.i(this, "onClose " + acceptable); - else - LogManager - .i(this, "onClose " + xmppConnection.hashCode() + " - " - + xmppConnection.connectionCounterValue + ", " - + acceptable); - ConnectionManager.getInstance().onDisconnect(connectionThread); - if (acceptable) - connectionThread.shutdown(); - return acceptable; - } - - /** - * Called when connection was closed for some reason. - */ - protected void onClose(ConnectionThread connectionThread) { - if (onDisconnect(connectionThread)) { - state = ConnectionState.waiting; - this.connectionThread = null; - if (connectionRequest) - Application.getInstance().onError(R.string.CONNECTION_FAILED); - connectionRequest = false; - } - } - - /** - * Called when another host should be used. - * - * @param connectionThread - * @param fqdn - * @param port - * @param useSrvLookup - */ - protected void onSeeOtherHost(ConnectionThread connectionThread, - String fqdn, int port, boolean useSrvLookup) { - // TODO: Check for number of redirects. - if (onDisconnect(connectionThread)) { - state = ConnectionState.connecting; - this.connectionThread = new ConnectionThread(this); - this.connectionThread.start(fqdn, port, useSrvLookup); - } - } + /** + * Connection options. + */ + private final ConnectionSettings connectionSettings; + + /** + * XMPP connection. + */ + private ConnectionThread connectionThread; + + /** + * Connection was requested by user. + */ + private boolean connectionRequest; + + /** + * Current state. + */ + private ConnectionState state; + + /** + * Whether force reconnection is in progress. + */ + private boolean disconnectionRequested; + + /** + * Need to register account on XMPP server. + */ + private boolean registerNewAccount; + + public ConnectionItem(AccountProtocol protocol, boolean custom, + String host, int port, String serverName, String userName, + String resource, boolean storePassword, String password, + boolean saslEnabled, TLSMode tlsMode, boolean compression, + ProxyType proxyType, String proxyHost, int proxyPort, + String proxyUser, String proxyPassword) { + connectionSettings = new ConnectionSettings(protocol, userName, + serverName, resource, custom, host, port, password, + saslEnabled, tlsMode, compression, proxyType, proxyHost, + proxyPort, proxyUser, proxyPassword); + connectionRequest = false; + disconnectionRequested = false; + connectionThread = null; + state = ConnectionState.offline; + } + + /** + * Register new account on server. + */ + public void registerAccount() { + registerNewAccount = true; + } + + /** + * Report if this connection is to register a new account on XMPP server. + */ + public boolean isRegisterAccount() { + return(registerNewAccount); + } + + /** + * Gets current connection thread. + * + * @return null if thread doesn't exists. + */ + public ConnectionThread getConnectionThread() { + return connectionThread; + } + + /** + * @return connection options. + */ + public ConnectionSettings getConnectionSettings() { + return connectionSettings; + } + + public ConnectionState getState() { + return state; + } + + /** + * Returns real full jid, that was assigned while login. + * + * @return null if connection is not established. + */ + public String getRealJid() { + ConnectionThread connectionThread = getConnectionThread(); + if (connectionThread == null) + return null; + XMPPConnection xmppConnection = connectionThread.getXMPPConnection(); + if (xmppConnection == null) + return null; + String user = xmppConnection.getUser(); + if (user == null) + return null; + return user; + } + + /** + * @param userRequest action was requested by user. + * @return Whether connection is available. + */ + protected boolean isConnectionAvailable(boolean userRequest) { + return true; + } + + /** + * Connect or disconnect from server depending on internal flags. + * + * @param userRequest action was requested by user. + * @return Whether state has been changed. + */ + public boolean updateConnection(boolean userRequest) { + boolean available = isConnectionAvailable(userRequest); + if (NetworkManager.getInstance().getState() != NetworkState.available + || !available || disconnectionRequested) { + ConnectionState target = available ? ConnectionState.waiting + : ConnectionState.offline; + if (state == ConnectionState.connected + || state == ConnectionState.authentication + || state == ConnectionState.connecting) { + if (userRequest) + connectionRequest = false; + if (connectionThread != null) { + disconnect(connectionThread); + // Force remove managed connection thread. + onClose(connectionThread); + connectionThread = null; + } + } else if (state == target) { + return false; + } + state = target; + return true; + } else { + if (state == ConnectionState.offline + || state == ConnectionState.waiting) { + if (userRequest) + connectionRequest = true; + state = ConnectionState.connecting; + connectionThread = new ConnectionThread(this); + if (connectionSettings.isCustom()) + connectionThread.start(connectionSettings.getHost(), + connectionSettings.getPort(), false, registerNewAccount); + else + connectionThread.start(connectionSettings.getServerName(), + 5222, true, registerNewAccount); + return true; + } else { + return false; + } + } + } + + /** + * Disconnect and connect using new connection. + */ + public void forceReconnect() { + if (!getState().isConnectable()) + return; + disconnectionRequested = true; + boolean request = connectionRequest; + connectionRequest = false; + updateConnection(false); + connectionRequest = request; + disconnectionRequested = false; + updateConnection(false); + } + + /** + * Starts disconnection in another thread. + */ + protected void disconnect(final ConnectionThread connectionThread) { + Thread thread = new Thread("Disconnection thread for " + this) { + @Override + public void run() { + XMPPConnection xmppConnection = connectionThread + .getXMPPConnection(); + if (xmppConnection != null) + try { + xmppConnection.disconnect(); + } catch (RuntimeException e) { + // connectionClose() in smack can fail. + } + } + + }; + thread.setPriority(Thread.MIN_PRIORITY); + thread.setDaemon(true); + thread.start(); + } + + /** + * @param connectionThread + * @return Whether thread is managed by connection. + */ + boolean isManaged(ConnectionThread connectionThread) { + return connectionThread == this.connectionThread; + } + + /** + * Update password. + * + * @param password + */ + protected void onPasswordChanged(String password) { + connectionSettings.setPassword(password); + } + + /** + * SRV record has been resolved. + */ + protected void onSRVResolved(ConnectionThread connectionThread) { + } + + /** + * Invalid certificate has been received. + */ + protected void onInvalidCertificate() { + } + + /** + * Connection has been established. + */ + protected void onConnected(ConnectionThread connectionThread) { + if (isRegisterAccount()) + state = ConnectionState.registration; + else if (isManaged(connectionThread)) + state = ConnectionState.authentication; + } + + /** + * New account has been registered on XMPP server. + */ + protected void onAccountRegistered(ConnectionThread connectionThread) { + registerNewAccount = false; + if (isManaged(connectionThread)) + state = ConnectionState.authentication; + } + + /** + * Authorization failed. + */ + protected void onAuthFailed() { + } + + /** + * Authorization passed. + */ + protected void onAuthorized(ConnectionThread connectionThread) { + if (isManaged(connectionThread)) + state = ConnectionState.connected; + } + + /** + * Called when disconnect should occur. + * + * @param connectionThread + * @return true if connection thread was managed. + */ + private boolean onDisconnect(ConnectionThread connectionThread) { + XMPPConnection xmppConnection = connectionThread.getXMPPConnection(); + boolean acceptable = isManaged(connectionThread); + if (xmppConnection == null) + LogManager.i(this, "onClose " + acceptable); + else + LogManager + .i(this, "onClose " + xmppConnection.hashCode() + " - " + + xmppConnection.connectionCounterValue + ", " + + acceptable); + ConnectionManager.getInstance().onDisconnect(connectionThread); + if (acceptable) + connectionThread.shutdown(); + return acceptable; + } + + /** + * Called when connection was closed for some reason. + */ + protected void onClose(ConnectionThread connectionThread) { + if (onDisconnect(connectionThread)) { + state = ConnectionState.waiting; + this.connectionThread = null; + if (connectionRequest) + Application.getInstance().onError(R.string.CONNECTION_FAILED); + connectionRequest = false; + } + } + + /** + * Called when another host should be used. + * + * @param connectionThread + * @param fqdn + * @param port + * @param useSrvLookup + */ + protected void onSeeOtherHost(ConnectionThread connectionThread, + String fqdn, int port, boolean useSrvLookup) { + // TODO: Check for number of redirects. + if (onDisconnect(connectionThread)) { + state = ConnectionState.connecting; + this.connectionThread = new ConnectionThread(this); + this.connectionThread.start(fqdn, port, useSrvLookup, registerNewAccount); + } + } } diff --git a/app/src/main/java/com/xabber/android/data/connection/ConnectionManager.java b/app/src/main/java/com/xabber/android/data/connection/ConnectionManager.java index 0d42d0179f..cdd2558ae5 100644 --- a/app/src/main/java/com/xabber/android/data/connection/ConnectionManager.java +++ b/app/src/main/java/com/xabber/android/data/connection/ConnectionManager.java @@ -1,36 +1,20 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data.connection; -import java.io.File; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.Iterator; -import java.util.Map.Entry; - -import org.jivesoftware.smack.Connection; -import org.jivesoftware.smack.ConnectionCreationListener; -import org.jivesoftware.smack.SASLAuthentication; -import org.jivesoftware.smack.SmackConfiguration; -import org.jivesoftware.smack.XMPPConnection; -import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smack.packet.IQ.Type; -import org.jivesoftware.smack.packet.Packet; -import org.jivesoftware.smackx.ServiceDiscoveryManager; - +import com.xabber.android.R; import com.xabber.android.data.Application; import com.xabber.android.data.LogManager; import com.xabber.android.data.NetworkException; @@ -40,268 +24,281 @@ import com.xabber.android.data.account.AccountItem; import com.xabber.android.data.account.AccountManager; import com.xabber.android.data.entity.NestedMap; -import com.xabber.androiddev.R; import com.xabber.xmpp.address.Jid; import com.xabber.xmpp.wlm.XMessengerOAuth2; +import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.ConnectionCreationListener; +import org.jivesoftware.smack.SASLAuthentication; +import org.jivesoftware.smack.SmackConfiguration; +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.IQ.Type; +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smackx.ServiceDiscoveryManager; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.Iterator; +import java.util.Map.Entry; + /** * Connection manager. - * + * * @author alexander.ivanov - * */ public class ConnectionManager implements OnInitializedListener, - OnCloseListener, OnTimerListener { - - /** - * Timeout for receiving reply from server. - */ - public final static int PACKET_REPLY_TIMEOUT = 5000; - - /** - * Path to the trust store in this system. - */ - public final static String TRUST_STORE_PATH; + OnCloseListener, OnTimerListener { - /** - * List of managed connection. Only managed connections can notify - * registered listeners. - */ - private final Collection managedConnections; + /** + * Timeout for receiving reply from server. + */ + public final static int PACKET_REPLY_TIMEOUT = 5000; - /** - * Request holders for its packet id in accounts. - */ - private final NestedMap requests; + /** + * Path to the trust store in this system. + */ + public final static String TRUST_STORE_PATH; + private final static ConnectionManager instance; - private final static ConnectionManager instance; + static { + instance = new ConnectionManager(); + Application.getInstance().addManager(instance); - static { - instance = new ConnectionManager(); - Application.getInstance().addManager(instance); + SmackConfiguration.setPacketReplyTimeout(PACKET_REPLY_TIMEOUT); - SmackConfiguration.setPacketReplyTimeout(PACKET_REPLY_TIMEOUT); + ServiceDiscoveryManager.setIdentityType("handheld"); + ServiceDiscoveryManager.setIdentityName(Application.getInstance() + .getString(R.string.client_name)); - ServiceDiscoveryManager.setIdentityType("handheld"); - ServiceDiscoveryManager.setIdentityName(Application.getInstance() - .getString(R.string.client_name)); + SASLAuthentication.registerSASLMechanism("X-MESSENGER-OAUTH2", + XMessengerOAuth2.class); + SASLAuthentication.supportSASLMechanism("X-MESSENGER-OAUTH2"); - SASLAuthentication.registerSASLMechanism("X-MESSENGER-OAUTH2", - XMessengerOAuth2.class); - SASLAuthentication.supportSASLMechanism("X-MESSENGER-OAUTH2"); + String path = System.getProperty("javax.net.ssl.trustStore"); + if (path == null) + TRUST_STORE_PATH = System.getProperty("java.home") + File.separator + + "etc" + File.separator + "security" + File.separator + + "cacerts.bks"; + else + TRUST_STORE_PATH = path; - String path = System.getProperty("javax.net.ssl.trustStore"); - if (path == null) - TRUST_STORE_PATH = System.getProperty("java.home") + File.separator - + "etc" + File.separator + "security" + File.separator - + "cacerts.bks"; - else - TRUST_STORE_PATH = path; + Connection + .addConnectionCreationListener(new ConnectionCreationListener() { + @Override + public void connectionCreated(final Connection connection) { + ServiceDiscoveryManager.getInstanceFor(connection) + .addFeature("sslc2s"); + } + }); + } - Connection - .addConnectionCreationListener(new ConnectionCreationListener() { - @Override - public void connectionCreated(final Connection connection) { - ServiceDiscoveryManager.getInstanceFor(connection) - .addFeature("sslc2s"); - } - }); - } + /** + * List of managed connection. Only managed connections can notify + * registered listeners. + */ + private final Collection managedConnections; + /** + * Request holders for its packet id in accounts. + */ + private final NestedMap requests; - public static ConnectionManager getInstance() { - return instance; - } + private ConnectionManager() { + managedConnections = new ArrayList(); + requests = new NestedMap(); + } - private ConnectionManager() { - managedConnections = new ArrayList(); - requests = new NestedMap(); - } + public static ConnectionManager getInstance() { + return instance; + } - @Override - public void onInitialized() { - updateConnections(false); - AccountManager.getInstance().onAccountsChanged( - new ArrayList(AccountManager.getInstance() - .getAllAccounts())); - } + @Override + public void onInitialized() { + updateConnections(false); + AccountManager.getInstance().onAccountsChanged( + new ArrayList(AccountManager.getInstance() + .getAllAccounts())); + } - @Override - public void onClose() { - ArrayList connections = new ArrayList( - managedConnections); - managedConnections.clear(); - for (ConnectionThread connectionThread : connections) - connectionThread.getConnectionItem().disconnect(connectionThread); - } + @Override + public void onClose() { + ArrayList connections = new ArrayList( + managedConnections); + managedConnections.clear(); + for (ConnectionThread connectionThread : connections) + connectionThread.getConnectionItem().disconnect(connectionThread); + } - /** - * Update connection state. - * - * Start connections in waiting states and stop invalidated connections. - * - * @param userRequest - */ - public void updateConnections(boolean userRequest) { - AccountManager accountManager = AccountManager.getInstance(); - for (String account : accountManager.getAccounts()) { - if (accountManager.getAccount(account) - .updateConnection(userRequest)) - AccountManager.getInstance().onAccountChanged(account); - } - } + /** + * Update connection state. + *

+ * Start connections in waiting states and stop invalidated connections. + * + * @param userRequest + */ + public void updateConnections(boolean userRequest) { + AccountManager accountManager = AccountManager.getInstance(); + for (String account : accountManager.getAccounts()) { + if (accountManager.getAccount(account) + .updateConnection(userRequest)) + AccountManager.getInstance().onAccountChanged(account); + } + } - /** - * Disconnect and connect using new network. - */ - public void forceReconnect() { - AccountManager accountManager = AccountManager.getInstance(); - for (String account : accountManager.getAccounts()) { - accountManager.getAccount(account).forceReconnect(); - AccountManager.getInstance().onAccountChanged(account); - } - } + /** + * Disconnect and connect using new network. + */ + public void forceReconnect() { + AccountManager accountManager = AccountManager.getInstance(); + for (String account : accountManager.getAccounts()) { + accountManager.getAccount(account).forceReconnect(); + AccountManager.getInstance().onAccountChanged(account); + } + } - /** - * Send packet to authenticated connection. - * - * @param account - * @param packet - */ - public void sendPacket(String account, Packet packet) - throws NetworkException { - ConnectionThread connectionThread = null; - for (ConnectionThread check : managedConnections) - if (check.getConnectionItem() instanceof AccountItem - && ((AccountItem) check.getConnectionItem()).getAccount() - .equals(account)) { - connectionThread = check; - break; - } - if (connectionThread == null - || !connectionThread.getConnectionItem().getState() - .isConnected()) - throw new NetworkException(R.string.NOT_CONNECTED); - XMPPConnection xmppConnection = connectionThread.getXMPPConnection(); - try { - xmppConnection.sendPacket(packet); - } catch (IllegalStateException e) { - throw new NetworkException(R.string.XMPP_EXCEPTION); - } - } + /** + * Send packet to authenticated connection. + * + * @param account + * @param packet + */ + public void sendPacket(String account, Packet packet) + throws NetworkException { + ConnectionThread connectionThread = null; + for (ConnectionThread check : managedConnections) + if (check.getConnectionItem() instanceof AccountItem + && ((AccountItem) check.getConnectionItem()).getAccount() + .equals(account)) { + connectionThread = check; + break; + } + if (connectionThread == null + || !connectionThread.getConnectionItem().getState() + .isConnected()) + throw new NetworkException(R.string.NOT_CONNECTED); + XMPPConnection xmppConnection = connectionThread.getXMPPConnection(); + try { + xmppConnection.sendPacket(packet); + } catch (IllegalStateException e) { + throw new NetworkException(R.string.XMPP_EXCEPTION); + } + } - /** - * Send packet to authenticated connection. And notify listener about - * acknowledgment. - * - * @param account - * @param iq - * @param listener - * @throws NetworkException - */ - public void sendRequest(String account, IQ iq, OnResponseListener listener) - throws NetworkException { - String packetId = iq.getPacketID(); - RequestHolder holder = new RequestHolder(listener); - sendPacket(account, iq); - requests.put(account, packetId, holder); - } + /** + * Send packet to authenticated connection. And notify listener about + * acknowledgment. + * + * @param account + * @param iq + * @param listener + * @throws NetworkException + */ + public void sendRequest(String account, IQ iq, OnResponseListener listener) + throws NetworkException { + String packetId = iq.getPacketID(); + RequestHolder holder = new RequestHolder(listener); + sendPacket(account, iq); + requests.put(account, packetId, holder); + } - public void onConnection(ConnectionThread connectionThread) { - managedConnections.add(connectionThread); - for (OnConnectionListener listener : Application.getInstance() - .getManagers(OnConnectionListener.class)) - listener.onConnection(connectionThread.getConnectionItem()); - } + public void onConnection(ConnectionThread connectionThread) { + managedConnections.add(connectionThread); + for (OnConnectionListener listener : Application.getInstance() + .getManagers(OnConnectionListener.class)) + listener.onConnection(connectionThread.getConnectionItem()); + } - public void onConnected(ConnectionThread connectionThread) { - if (!managedConnections.contains(connectionThread)) - return; - for (OnConnectedListener listener : Application.getInstance() - .getManagers(OnConnectedListener.class)) - listener.onConnected(connectionThread.getConnectionItem()); - } + public void onConnected(ConnectionThread connectionThread) { + if (!managedConnections.contains(connectionThread)) + return; + for (OnConnectedListener listener : Application.getInstance() + .getManagers(OnConnectedListener.class)) + listener.onConnected(connectionThread.getConnectionItem()); + } - public void onAuthorized(ConnectionThread connectionThread) { - if (!managedConnections.contains(connectionThread)) - return; - LogManager.i(this, - "onAuthorized: " + connectionThread.getConnectionItem()); - for (OnAuthorizedListener listener : Application.getInstance() - .getManagers(OnAuthorizedListener.class)) - listener.onAuthorized(connectionThread.getConnectionItem()); - } + public void onAuthorized(ConnectionThread connectionThread) { + if (!managedConnections.contains(connectionThread)) + return; + LogManager.i(this, + "onAuthorized: " + connectionThread.getConnectionItem()); + for (OnAuthorizedListener listener : Application.getInstance() + .getManagers(OnAuthorizedListener.class)) + listener.onAuthorized(connectionThread.getConnectionItem()); + } - public void onDisconnect(ConnectionThread connectionThread) { - if (!managedConnections.remove(connectionThread)) - return; - ConnectionItem connectionItem = connectionThread.getConnectionItem(); - if (connectionItem instanceof AccountItem) { - String account = ((AccountItem) connectionItem).getAccount(); - for (Entry entry : requests.getNested( - account).entrySet()) - entry.getValue().getListener() - .onDisconnect(account, entry.getKey()); - requests.clear(account); - } - for (OnDisconnectListener listener : Application.getInstance() - .getManagers(OnDisconnectListener.class)) - listener.onDisconnect(connectionThread.getConnectionItem()); - } + public void onDisconnect(ConnectionThread connectionThread) { + if (!managedConnections.remove(connectionThread)) + return; + ConnectionItem connectionItem = connectionThread.getConnectionItem(); + if (connectionItem instanceof AccountItem) { + String account = ((AccountItem) connectionItem).getAccount(); + for (Entry entry : requests.getNested( + account).entrySet()) + entry.getValue().getListener() + .onDisconnect(account, entry.getKey()); + requests.clear(account); + } + for (OnDisconnectListener listener : Application.getInstance() + .getManagers(OnDisconnectListener.class)) + listener.onDisconnect(connectionThread.getConnectionItem()); + } - public void processPacket(ConnectionThread connectionThread, Packet packet) { - if (!managedConnections.contains(connectionThread)) - return; - ConnectionItem connectionItem = connectionThread.getConnectionItem(); - if (packet instanceof IQ && connectionItem instanceof AccountItem) { - IQ iq = (IQ) packet; - String packetId = iq.getPacketID(); - if (packetId != null - && (iq.getType() == Type.RESULT || iq.getType() == Type.ERROR)) { - String account = ((AccountItem) connectionItem).getAccount(); - RequestHolder requestHolder = requests - .remove(account, packetId); - if (requestHolder != null) { - if (iq.getType() == Type.RESULT) - requestHolder.getListener().onReceived(account, - packetId, iq); - else - requestHolder.getListener().onError(account, packetId, - iq); - } - } - } - for (OnPacketListener listener : Application.getInstance().getManagers( - OnPacketListener.class)) - listener.onPacket(connectionItem, - Jid.getBareAddress(packet.getFrom()), packet); - } + public void processPacket(ConnectionThread connectionThread, Packet packet) { + if (!managedConnections.contains(connectionThread)) + return; + ConnectionItem connectionItem = connectionThread.getConnectionItem(); + if (packet instanceof IQ && connectionItem instanceof AccountItem) { + IQ iq = (IQ) packet; + String packetId = iq.getPacketID(); + if (packetId != null + && (iq.getType() == Type.RESULT || iq.getType() == Type.ERROR)) { + String account = ((AccountItem) connectionItem).getAccount(); + RequestHolder requestHolder = requests + .remove(account, packetId); + if (requestHolder != null) { + if (iq.getType() == Type.RESULT) + requestHolder.getListener().onReceived(account, + packetId, iq); + else + requestHolder.getListener().onError(account, packetId, + iq); + } + } + } + for (OnPacketListener listener : Application.getInstance().getManagers( + OnPacketListener.class)) + listener.onPacket(connectionItem, + Jid.getBareAddress(packet.getFrom()), packet); + } - @Override - public void onTimer() { - if (NetworkManager.getInstance().getState() != NetworkState.suspended) { - Collection reconnect = new ArrayList(); - for (ConnectionThread connectionThread : managedConnections) - if (connectionThread.getConnectionItem().getState() - .isConnected() - // XMPPConnection can`t be null here - && !connectionThread.getXMPPConnection().isAlive()) { - LogManager.i(connectionThread.getConnectionItem(), - "forceReconnect on checkAlive"); - reconnect.add(connectionThread.getConnectionItem()); - } - for (ConnectionItem connection : reconnect) - connection.forceReconnect(); - } - long now = new Date().getTime(); - Iterator> iterator = requests.iterator(); - while (iterator.hasNext()) { - NestedMap.Entry entry = iterator.next(); - if (entry.getValue().isExpired(now)) { - entry.getValue().getListener() - .onTimeout(entry.getFirst(), entry.getSecond()); - iterator.remove(); - } - } - } + @Override + public void onTimer() { + if (NetworkManager.getInstance().getState() != NetworkState.suspended) { + Collection reconnect = new ArrayList(); + for (ConnectionThread connectionThread : managedConnections) + if (connectionThread.getConnectionItem().getState() + .isConnected() + // XMPPConnection can`t be null here + && !connectionThread.getXMPPConnection().isAlive()) { + LogManager.i(connectionThread.getConnectionItem(), + "forceReconnect on checkAlive"); + reconnect.add(connectionThread.getConnectionItem()); + } + for (ConnectionItem connection : reconnect) + connection.forceReconnect(); + } + long now = new Date().getTime(); + Iterator> iterator = requests.iterator(); + while (iterator.hasNext()) { + NestedMap.Entry entry = iterator.next(); + if (entry.getValue().isExpired(now)) { + entry.getValue().getListener() + .onTimeout(entry.getFirst(), entry.getSecond()); + iterator.remove(); + } + } + } } diff --git a/app/src/main/java/com/xabber/android/data/connection/ConnectionSettings.java b/app/src/main/java/com/xabber/android/data/connection/ConnectionSettings.java index f8de6f0fc1..f09b00124f 100644 --- a/app/src/main/java/com/xabber/android/data/connection/ConnectionSettings.java +++ b/app/src/main/java/com/xabber/android/data/connection/ConnectionSettings.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,222 +18,221 @@ /** * Settings for connection. - * + * * @author alexander.ivanov - * */ public class ConnectionSettings { - /** - * Protocol. - */ - private final AccountProtocol protocol; - - /** - * User part of jid. - */ - private final String userName; - - /** - * Server part of jid. - */ - private final String serverName; - - /** - * Resource part of jid. - */ - private final String resource; - - /** - * Use custom connection host and port. - */ - private boolean custom; - - /** - * Host for connection. - */ - private String host; - - /** - * Port for connection. - */ - private int port; - - /** - * Password. - */ - private String password; - - /** - * Whether SASL Authentication Enabled. - */ - private boolean saslEnabled; - - /** - * TLS mode. - */ - private TLSMode tlsMode; - - /** - * Use compression. - */ - private boolean compression; - - private ProxyType proxyType; - - private String proxyHost; - - private int proxyPort; - - private String proxyUser; - - private String proxyPassword; - - public ConnectionSettings(AccountProtocol protocol, String userName, - String serverName, String resource, boolean custom, String host, - int port, String password, boolean saslEnabled, TLSMode tlsMode, - boolean compression, ProxyType proxyType, String proxyHost, - int proxyPort, String proxyUser, String proxyPassword) { - super(); - this.protocol = protocol; - this.userName = userName; - this.serverName = serverName; - this.resource = resource; - this.custom = custom; - this.host = host; - this.port = port; - this.password = password; - this.saslEnabled = saslEnabled; - this.tlsMode = tlsMode; - this.compression = compression; - this.proxyType = proxyType; - this.proxyHost = proxyHost; - this.proxyPort = proxyPort; - this.proxyUser = proxyUser; - this.proxyPassword = proxyPassword; - } - - public AccountProtocol getProtocol() { - return protocol; - } - - /** - * @return User part of jid. - */ - public String getUserName() { - return userName; - } - - /** - * @return Server part of jid. - */ - public String getServerName() { - return serverName; - } - - /** - * @return Whether custom host and port must be used. - */ - public boolean isCustom() { - return custom; - } - - /** - * @return Custom host to connect to. - */ - public String getHost() { - return host; - } - - public int getPort() { - return port; - } - - public String getResource() { - return resource; - } - - public String getPassword() { - return password; - } - - /** - * @return Whether SASL Authentication Enabled. - */ - public boolean isSaslEnabled() { - return saslEnabled; - } - - /** - * @return TLS mode. - */ - public TLSMode getTlsMode() { - return tlsMode; - } - - /** - * @return Whether compression is used. - */ - public boolean useCompression() { - return compression; - } - - public ProxyType getProxyType() { - return proxyType; - } - - public String getProxyHost() { - return proxyHost; - } - - public int getProxyPort() { - return proxyPort; - } - - public String getProxyUser() { - return proxyUser; - } - - public String getProxyPassword() { - return proxyPassword; - } - - /** - * Updates options. - * - * @param custom - * @param host - * @param port - * @param password - * @param saslEnabled - * @param tlsMode - * @param compression - */ - public void update(boolean custom, String host, int port, String password, - boolean saslEnabled, TLSMode tlsMode, boolean compression, - ProxyType proxyType, String proxyHost, int proxyPort, - String proxyUser, String proxyPassword) { - this.custom = custom; - this.host = host; - this.port = port; - this.password = password; - this.saslEnabled = saslEnabled; - this.tlsMode = tlsMode; - this.compression = compression; - this.proxyType = proxyType; - this.proxyHost = proxyHost; - this.proxyPort = proxyPort; - this.proxyUser = proxyUser; - this.proxyPassword = proxyPassword; - } - - /** - * Sets password. - * - * @param password - */ - public void setPassword(String password) { - this.password = password; - } + /** + * Protocol. + */ + private final AccountProtocol protocol; + + /** + * User part of jid. + */ + private final String userName; + + /** + * Server part of jid. + */ + private final String serverName; + + /** + * Resource part of jid. + */ + private final String resource; + + /** + * Use custom connection host and port. + */ + private boolean custom; + + /** + * Host for connection. + */ + private String host; + + /** + * Port for connection. + */ + private int port; + + /** + * Password. + */ + private String password; + + /** + * Whether SASL Authentication Enabled. + */ + private boolean saslEnabled; + + /** + * TLS mode. + */ + private TLSMode tlsMode; + + /** + * Use compression. + */ + private boolean compression; + + private ProxyType proxyType; + + private String proxyHost; + + private int proxyPort; + + private String proxyUser; + + private String proxyPassword; + + public ConnectionSettings(AccountProtocol protocol, String userName, + String serverName, String resource, boolean custom, String host, + int port, String password, boolean saslEnabled, TLSMode tlsMode, + boolean compression, ProxyType proxyType, String proxyHost, + int proxyPort, String proxyUser, String proxyPassword) { + super(); + this.protocol = protocol; + this.userName = userName; + this.serverName = serverName; + this.resource = resource; + this.custom = custom; + this.host = host; + this.port = port; + this.password = password; + this.saslEnabled = saslEnabled; + this.tlsMode = tlsMode; + this.compression = compression; + this.proxyType = proxyType; + this.proxyHost = proxyHost; + this.proxyPort = proxyPort; + this.proxyUser = proxyUser; + this.proxyPassword = proxyPassword; + } + + public AccountProtocol getProtocol() { + return protocol; + } + + /** + * @return User part of jid. + */ + public String getUserName() { + return userName; + } + + /** + * @return Server part of jid. + */ + public String getServerName() { + return serverName; + } + + /** + * @return Whether custom host and port must be used. + */ + public boolean isCustom() { + return custom; + } + + /** + * @return Custom host to connect to. + */ + public String getHost() { + return host; + } + + public int getPort() { + return port; + } + + public String getResource() { + return resource; + } + + public String getPassword() { + return password; + } + + /** + * @return Whether SASL Authentication Enabled. + */ + public boolean isSaslEnabled() { + return saslEnabled; + } + + /** + * @return TLS mode. + */ + public TLSMode getTlsMode() { + return tlsMode; + } + + /** + * @return Whether compression is used. + */ + public boolean useCompression() { + return compression; + } + + public ProxyType getProxyType() { + return proxyType; + } + + public String getProxyHost() { + return proxyHost; + } + + public int getProxyPort() { + return proxyPort; + } + + public String getProxyUser() { + return proxyUser; + } + + public String getProxyPassword() { + return proxyPassword; + } + + /** + * Updates options. + * + * @param custom + * @param host + * @param port + * @param password + * @param saslEnabled + * @param tlsMode + * @param compression + */ + public void update(boolean custom, String host, int port, String password, + boolean saslEnabled, TLSMode tlsMode, boolean compression, + ProxyType proxyType, String proxyHost, int proxyPort, + String proxyUser, String proxyPassword) { + this.custom = custom; + this.host = host; + this.port = port; + this.password = password; + this.saslEnabled = saslEnabled; + this.tlsMode = tlsMode; + this.compression = compression; + this.proxyType = proxyType; + this.proxyHost = proxyHost; + this.proxyPort = proxyPort; + this.proxyUser = proxyUser; + this.proxyPassword = proxyPassword; + } + + /** + * Sets password. + * + * @param password + */ + public void setPassword(String password) { + this.password = password; + } } diff --git a/app/src/main/java/com/xabber/android/data/connection/ConnectionState.java b/app/src/main/java/com/xabber/android/data/connection/ConnectionState.java index 6f6d11a527..45c5d40907 100644 --- a/app/src/main/java/com/xabber/android/data/connection/ConnectionState.java +++ b/app/src/main/java/com/xabber/android/data/connection/ConnectionState.java @@ -1,85 +1,91 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data.connection; -import com.xabber.androiddev.R; +import com.xabber.android.R; /** * State of connection. - * + * * @author alexander.ivanov - * */ public enum ConnectionState { - /** - * Connection is not active. - */ - offline, + /** + * Connection is not active. + */ + offline, - /** - * Waiting for connection before first connection or before reconnection. - */ - waiting, + /** + * Waiting for connection before first connection or before reconnection. + */ + waiting, - /** - * Connection is in progress. - */ - connecting, + /** + * Connection is in progress. + */ + connecting, - /** - * Connection was established, authentication is in progress. - */ - authentication, + /** + * Connection was established, registration is in progress. + */ + registration, - /** - * Authorized connection has been established. - */ - connected; + /** + * Connection was established, authentication is in progress. + */ + authentication, - /** - * @return whether authorized connection has been established. - */ - public boolean isConnected() { - return this == ConnectionState.connected; - } + /** + * Authorized connection has been established. + */ + connected; - /** - * @return whether connection has already been established or will be - * established later. - */ - public boolean isConnectable() { - return this != ConnectionState.offline; - } + /** + * @return whether authorized connection has been established. + */ + public boolean isConnected() { + return this == ConnectionState.connected; + } - /** - * @return Resource id with associated string. - */ - public int getStringId() { - if (this == ConnectionState.offline) - return R.string.account_state_offline; - else if (this == ConnectionState.waiting) - return R.string.account_state_waiting; - else if (this == ConnectionState.connecting) - return R.string.account_state_connecting; - else if (this == ConnectionState.authentication) - return R.string.account_state_authentication; - else if (this == ConnectionState.connected) - return R.string.account_state_connected; - else - throw new IllegalStateException(); - } + /** + * @return whether connection has already been established or will be + * established later. + */ + public boolean isConnectable() { + return this != ConnectionState.offline; + } + + /** + * @return Resource id with associated string. + */ + public int getStringId() { + if (this == ConnectionState.offline) + return R.string.account_state_offline; + else if (this == ConnectionState.waiting) + return R.string.account_state_waiting; + else if (this == ConnectionState.connecting) + return R.string.account_state_connecting; + else if (this == ConnectionState.registration) + return R.string.account_state_registration; + else if (this == ConnectionState.authentication) + return R.string.account_state_authentication; + else if (this == ConnectionState.connected) + return R.string.account_state_connected; + else + throw new IllegalStateException(); + } } diff --git a/app/src/main/java/com/xabber/android/data/connection/ConnectionThread.java b/app/src/main/java/com/xabber/android/data/connection/ConnectionThread.java index a57ce86d14..3511dcb1a9 100644 --- a/app/src/main/java/com/xabber/android/data/connection/ConnectionThread.java +++ b/app/src/main/java/com/xabber/android/data/connection/ConnectionThread.java @@ -1,19 +1,21 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data.connection; +import android.os.Build; + import java.net.InetAddress; import java.security.cert.CertificateException; import java.util.concurrent.ExecutorService; @@ -45,644 +47,723 @@ /** * Provides connection workflow. - * + *

* Warning: SMACK with its connection is going to be removed! - * + * * @author alexander.ivanov - * */ public class ConnectionThread implements - org.jivesoftware.smack.ConnectionListener, - org.jivesoftware.smack.PacketListener { - - private static Pattern ADDRESS_AND_PORT = Pattern.compile("^(.*):(\\d+)$"); - - /** - * Filter to process all packets. - */ - private final AcceptAll ACCEPT_ALL = new AcceptAll(); - - private final ConnectionItem connectionItem; - - /** - * SMACK connection. - */ - private XMPPConnection xmppConnection; - - /** - * Thread holder for this connection. - */ - private final ExecutorService executorService; - - private final AccountProtocol protocol; - - private final String serverName; - - private final String login; - - /** - * Refresh token for OAuth or regular password. - */ - private final String token; - - private final String resource; - - private final boolean saslEnabled; - - private final TLSMode tlsMode; - - private final boolean compression; - - private final ProxyType proxyType; - - private final String proxyHost; - - private final int proxyPort; - - private final String proxyUser; - - private final String proxyPassword; - - private boolean started; - - public ConnectionThread(final ConnectionItem connectionItem) { - this.connectionItem = connectionItem; - executorService = Executors - .newSingleThreadExecutor(new ThreadFactory() { - @Override - public Thread newThread(Runnable runnable) { - Thread thread = new Thread( - runnable, - "Connection thread for " - + (connectionItem instanceof AccountItem ? ((AccountItem) connectionItem) - .getAccount() : connectionItem)); - thread.setPriority(Thread.MIN_PRIORITY); - thread.setDaemon(true); - return thread; - } - }); - ConnectionManager.getInstance().onConnection(this); - ConnectionSettings connectionSettings = connectionItem - .getConnectionSettings(); - protocol = connectionSettings.getProtocol(); - serverName = connectionSettings.getServerName(); - token = connectionSettings.getPassword(); - resource = connectionSettings.getResource(); - saslEnabled = connectionSettings.isSaslEnabled(); - tlsMode = connectionSettings.getTlsMode(); - compression = connectionSettings.useCompression(); - if (saslEnabled && protocol == AccountProtocol.gtalk) - login = connectionSettings.getUserName() + "@" - + connectionSettings.getServerName(); - else - login = connectionSettings.getUserName(); - proxyType = connectionSettings.getProxyType(); - proxyHost = connectionSettings.getProxyHost(); - proxyPort = connectionSettings.getProxyPort(); - proxyUser = connectionSettings.getProxyUser(); - proxyPassword = connectionSettings.getProxyPassword(); - started = false; - } - - public XMPPConnection getXMPPConnection() { - return xmppConnection; - } - - public ConnectionItem getConnectionItem() { - return connectionItem; - } - - /** - * Resolve SRV record. - * - * @param fqdn - * @param defaultHost - * @param defaultPort - */ - private void srvResolve(final String fqdn, final String defaultHost, - final int defaultPort) { - final Record[] records = DNSManager.getInstance().fetchRecords(fqdn); - runOnUiThread(new Runnable() { - @Override - public void run() { - onSRVResolved(fqdn, defaultHost, defaultPort, records); - } - }); - } - - /** - * Called when srv records are resolved. - * - * @param fqdn - * @param defaultHost - * @param defaultPort - * @param records - */ - private void onSRVResolved(final String fqdn, final String defaultHost, - final int defaultPort, Record[] records) { - DNSManager.getInstance().onRecordsReceived(fqdn, records); - final Target target = DNSManager.getInstance().getCurrentTarget(fqdn); - if (target == null) { - LogManager.i(this, "Use defaults"); - runOnConnectionThread(new Runnable() { - @Override - public void run() { - addressResolve(null, defaultHost, defaultPort, true); - } - }); - } else { - runOnConnectionThread(new Runnable() { - @Override - public void run() { - addressResolve(fqdn, target.getHost(), target.getPort(), - true); - } - }); - } - } - - /** - * Resolves address to connect to. - * - * @param fqdn - * server name of subsequence SRV lookup. Should be - * null for custom host work flow. - * @param host - * @param port - * @param firstRequest - */ - private void addressResolve(final String fqdn, final String host, - final int port, final boolean firstRequest) { - LogManager.i(this, "Resolve " + host + ":" + port); - final InetAddress[] addresses = DNSManager.getInstance() - .fetchAddresses(host); - runOnUiThread(new Runnable() { - @Override - public void run() { - onAddressResolved(fqdn, host, port, firstRequest, addresses); - } - }); - } - - /** - * Called when address resolved are resolved. - * - * @param fqdn - * @param host - * @param port - * @param firstRequest - * @param addresses - */ - private void onAddressResolved(final String fqdn, final String host, - final int port, boolean firstRequest, final InetAddress[] addresses) { - DNSManager.getInstance().onAddressesReceived(host, addresses); - InetAddress address = DNSManager.getInstance().getNextAddress(host); - if (address != null) { - onReady(address, port); - return; - } - if (fqdn == null) { - if (firstRequest) { - onAddressResolved(null, host, port, false, addresses); - return; - } - } else { - DNSManager.getInstance().getNextTarget(fqdn); - if (DNSManager.getInstance().getCurrentTarget(fqdn) == null - && firstRequest) - DNSManager.getInstance().getNextTarget(fqdn); - final Target target = DNSManager.getInstance().getCurrentTarget( - fqdn); - if (target != null) { - runOnConnectionThread(new Runnable() { - @Override - public void run() { - addressResolve(fqdn, target.getHost(), - target.getPort(), false); - } - }); - return; - } - } - XMPPException exception = new XMPPException( - "There is no available address."); - LogManager.exception(this, exception); - connectionClosedOnError(exception); - } - - /** - * Called when configuration is ready and xmpp connection instance can be - * created. - * - * @param records - */ - private void onReady(final InetAddress address, final int port) { - LogManager.i(this, "Use " + address); - ProxyInfo proxy = proxyType.getProxyInfo(proxyHost, proxyPort, - proxyUser, proxyPassword); - ConnectionConfiguration connectionConfiguration = new ConnectionConfiguration( - address.getHostAddress(), port, serverName, proxy); - if (Application.SDK_INT >= 14) { - connectionConfiguration.setTruststoreType("AndroidCAStore"); - connectionConfiguration.setTruststorePassword(null); - connectionConfiguration.setTruststorePath(null); - } else { - connectionConfiguration.setTruststoreType("BKS"); - connectionConfiguration - .setTruststorePath(ConnectionManager.TRUST_STORE_PATH); - } - // Disable smack`s reconnection. - connectionConfiguration.setReconnectionAllowed(false); - // We will send custom presence. - connectionConfiguration.setSendPresence(false); - // We use own roster management. - connectionConfiguration.setRosterLoadedAtLogin(false); - - if (SettingsManager.securityCheckCertificate()) { - connectionConfiguration.setExpiredCertificatesCheckEnabled(true); - connectionConfiguration.setNotMatchingDomainCheckEnabled(true); - connectionConfiguration.setSelfSignedCertificateEnabled(false); - connectionConfiguration.setVerifyChainEnabled(true); - connectionConfiguration.setVerifyRootCAEnabled(true); - connectionConfiguration.setCertificateListener(CertificateManager - .getInstance().createCertificateListener(connectionItem)); - } else { - connectionConfiguration.setExpiredCertificatesCheckEnabled(false); - connectionConfiguration.setNotMatchingDomainCheckEnabled(false); - connectionConfiguration.setSelfSignedCertificateEnabled(true); - connectionConfiguration.setVerifyChainEnabled(false); - connectionConfiguration.setVerifyRootCAEnabled(false); - } - - connectionConfiguration.setSASLAuthenticationEnabled(saslEnabled); - connectionConfiguration.setSecurityMode(tlsMode.getSecurityMode()); - connectionConfiguration.setCompressionEnabled(compression); - - xmppConnection = new XMPPConnection(connectionConfiguration); - xmppConnection.addPacketListener(this, ACCEPT_ALL); - xmppConnection.forceAddConnectionListener(this); - connectionItem.onSRVResolved(this); - final String password = OAuthManager.getInstance().getPassword( - protocol, token); - if (password != null) { - runOnConnectionThread(new Runnable() { - @Override - public void run() { - connect(password); - } - }); - } else { - runOnConnectionThread(new Runnable() { - @Override - public void run() { - passwordRequest(); - } - }); - } - } - - /** - * Request to renew password using OAuth. - */ - private void passwordRequest() { - final OAuthResult oAuthResult; - try { - oAuthResult = OAuthManager.getInstance().requestAccessToken( - protocol, token); - } catch (NetworkException e) { - throw new RuntimeException(e); - } - runOnUiThread(new Runnable() { - @Override - public void run() { - onPasswordReceived(oAuthResult); - } - }); - } - - /** - * Called when password has been renewed. - * - * @param oAuthResult - */ - private void onPasswordReceived(final OAuthResult oAuthResult) { - OAuthManager.getInstance().onAccessTokenReceived(oAuthResult); - if (oAuthResult == null) { - connectionItem.onAuthFailed(); - return; - } - connectionItem.onPasswordChanged(oAuthResult.getRefreshToken()); - final String password = oAuthResult.getAccessToken(); - runOnConnectionThread(new Runnable() { - @Override - public void run() { - connect(password); - } - }); - } - - /** - * Server requests us to see another host. - * - * @param target - */ - private void seeOtherHost(String target) { - Matcher matcher = ADDRESS_AND_PORT.matcher(target); - int defaultPort = 5222; - if (matcher.matches()) { - String value = matcher.group(2); - try { - defaultPort = Integer.valueOf(value); - target = matcher.group(1); - } catch (NumberFormatException e2) { - } - } - final String fqdn = target; - final int port = defaultPort; - // TODO: Check for the same address. - runOnUiThread(new Runnable() { - @Override - public void run() { - connectionItem.onSeeOtherHost(ConnectionThread.this, fqdn, - port, true); - } - }); - } - - /** - * Connect to the server. - * - * @param password - */ - private void connect(final String password) { - try { - xmppConnection.connect(); - } catch (XMPPException e) { - checkForCertificateError(e); - if (!checkForSeeOtherHost(e)) { - // There is no connection listeners yet, so we call onClose. - throw new RuntimeException(e); - } - } catch (IllegalStateException e) { - // There is no connection listeners yet, so we call onClose. - // connectionCreated() in other modules can fail. - throw new RuntimeException(e); - } - runOnUiThread(new Runnable() { - @Override - public void run() { - onConnected(password); - } - }); - } - - /** - * Check for certificate exception. - * - * @param e - */ - private void checkForCertificateError(Exception e) { - if (!(e instanceof XMPPException)) - return; - Throwable e1 = ((XMPPException) e).getWrappedThrowable(); - if (!(e1 instanceof SSLException)) - return; - Throwable e2 = ((SSLException) e1).getCause(); - if (!(e2 instanceof CertificateException)) - return; - runOnUiThread(new Runnable() { - @Override - public void run() { - connectionItem.onInvalidCertificate(); - } - }); - } - - /** - * Check for see other host exception. - * - * @param e - * @return true if {@link StreamError.Type#seeOtherHost} has - * been found. - */ - private boolean checkForSeeOtherHost(Exception e) { - if (!(e instanceof XMPPException)) - return false; - StreamError streamError = ((XMPPException) e).getStreamError(); - if (streamError == null - || streamError.getType() != StreamError.Type.seeOtherHost) - return false; - String target = streamError.getBody(); - if (target == null || "".equals(target)) - return false; - LogManager.i(this, "See other host: " + target); - seeOtherHost(target); - return true; - } - - /** - * Connection to the server has been established. - * - * @param password - */ - private void onConnected(final String password) { - connectionItem.onConnected(this); - ConnectionManager.getInstance().onConnected(this); - runOnConnectionThread(new Runnable() { - @Override - public void run() { - authorization(password); - } - }); - } - - /** - * Login. - * - * @param password - */ - private void authorization(String password) { - try { - xmppConnection.login(login, password, resource); - } catch (IllegalStateException e) { - // onClose must be called from reader thread. - return; - } catch (XMPPException e) { - LogManager.exception(connectionItem, e); - // SASLAuthentication#authenticate(String,String,String) - boolean SASLfailed = e.getMessage() != null - && e.getMessage().startsWith("SASL authentication ") - && !e.getMessage().endsWith("temporary-auth-failure"); - // NonSASLAuthentication#authenticate(String,String,String) - // Authentication request failed (doesn't supported), - // error was returned after negotiation or - // authentication failed. - boolean NonSASLfailed = e.getXMPPError() != null - && "Authentication failed.".equals(e.getMessage()); - if (SASLfailed || NonSASLfailed) { - // connectionClosed can be called before from reader thread, - // so don't check whether connection is managed. - Application.getInstance().runOnUiThread(new Runnable() { - @Override - public void run() { - // Login failed. We don`t want to reconnect. - connectionItem.onAuthFailed(); - } - }); - connectionClosed(); - } else - connectionClosedOnError(e); - // Server will destroy connection, but we can speedup - // it. - xmppConnection.disconnect(); - return; - } - runOnUiThread(new Runnable() { - @Override - public void run() { - onAuthorized(); - } - }); - } - - /** - * Authorization passed. - */ - private void onAuthorized() { - connectionItem.onAuthorized(this); - ConnectionManager.getInstance().onAuthorized(this); - if (connectionItem instanceof AccountItem) - AccountManager.getInstance().removeAuthorizationError( - ((AccountItem) connectionItem).getAccount()); - shutdown(); - } - - @Override - public void connectionClosed() { - // Can be called on error, e.g. XMPPConnection#initConnection(). - runOnUiThread(new Runnable() { - @Override - public void run() { - connectionItem.onClose(ConnectionThread.this); - } - }); - } - - @Override - public void connectionClosedOnError(Exception e) { - checkForCertificateError(e); - if (checkForSeeOtherHost(e)) - return; - connectionClosed(); - } - - @Override - public void reconnectingIn(int seconds) { - } - - @Override - public void reconnectionSuccessful() { - } - - @Override - public void reconnectionFailed(Exception e) { - } - - @Override - public void processPacket(final Packet packet) { - Application.getInstance().runOnUiThread(new Runnable() { - @Override - public void run() { - ConnectionManager.getInstance().processPacket( - ConnectionThread.this, packet); - } - }); - } - - /** - * Filter to accept all packets. - * - * @author alexander.ivanov - */ - static class AcceptAll implements PacketFilter { - @Override - public boolean accept(Packet packet) { - return true; - } - } - - /** - * Start connection. - * - * This function can be called only once. - * - * @param fqdn - * Fully Qualified Domain Names. - * @param port - * Preferred port. - * @param useSRVLookup - * Whether SRV lookup should be used. - */ - synchronized void start(final String fqdn, final int port, - final boolean useSRVLookup) { - if (started) - throw new IllegalStateException(); - started = true; - runOnConnectionThread(new Runnable() { - @Override - public void run() { - if (useSRVLookup) - srvResolve(fqdn, fqdn, port); - else - addressResolve(null, fqdn, port, true); - } - }); - } - - /** - * Stop connection. - * - * {@link #start()} MUST BE CALLED FIRST. - */ - void shutdown() { - executorService.shutdownNow(); - } - - /** - * Submit task to be executed in connection thread. - * - * @param runnable - */ - private void runOnConnectionThread(final Runnable runnable) { - executorService.submit(new Runnable() { - @Override - public void run() { - if (!connectionItem.isManaged(ConnectionThread.this)) - return; - try { - runnable.run(); - } catch (RuntimeException e) { - LogManager.exception(connectionItem, e); - connectionClosedOnError(e); - } - } - }); - } - - /** - * Commit changes received from connection thread in UI thread. - * - * @param runnable - */ - private void runOnUiThread(final Runnable runnable) { - Application.getInstance().runOnUiThread(new Runnable() { - @Override - public void run() { - if (!connectionItem.isManaged(ConnectionThread.this)) - return; - runnable.run(); - } - }); - } + org.jivesoftware.smack.ConnectionListener, + org.jivesoftware.smack.PacketListener { + + private static Pattern ADDRESS_AND_PORT = Pattern.compile("^(.*):(\\d+)$"); + + /** + * Filter to process all packets. + */ + private final AcceptAll ACCEPT_ALL = new AcceptAll(); + + private final ConnectionItem connectionItem; + + /** + * SMACK connection. + */ + private XMPPConnection xmppConnection; + + /** + * Thread holder for this connection. + */ + private final ExecutorService executorService; + + private final AccountProtocol protocol; + + private final String serverName; + + private final String login; + + /** + * Refresh token for OAuth or regular password. + */ + private final String token; + + private final String resource; + + private final boolean saslEnabled; + + private final TLSMode tlsMode; + + private final boolean compression; + + private final ProxyType proxyType; + + private final String proxyHost; + + private final int proxyPort; + + private final String proxyUser; + + private final String proxyPassword; + + private boolean started; + + private boolean registerNewAccount; + + public ConnectionThread(final ConnectionItem connectionItem) { + this.connectionItem = connectionItem; + executorService = Executors + .newSingleThreadExecutor(new ThreadFactory() { + @Override + public Thread newThread(Runnable runnable) { + Thread thread = new Thread( + runnable, + "Connection thread for " + + (connectionItem instanceof AccountItem ? ((AccountItem) connectionItem) + .getAccount() : connectionItem)); + thread.setPriority(Thread.MIN_PRIORITY); + thread.setDaemon(true); + return thread; + } + }); + ConnectionManager.getInstance().onConnection(this); + ConnectionSettings connectionSettings = connectionItem + .getConnectionSettings(); + protocol = connectionSettings.getProtocol(); + serverName = connectionSettings.getServerName(); + token = connectionSettings.getPassword(); + resource = connectionSettings.getResource(); + saslEnabled = connectionSettings.isSaslEnabled(); + tlsMode = connectionSettings.getTlsMode(); + compression = connectionSettings.useCompression(); + if (saslEnabled && protocol == AccountProtocol.gtalk) + login = connectionSettings.getUserName() + "@" + + connectionSettings.getServerName(); + else + login = connectionSettings.getUserName(); + proxyType = connectionSettings.getProxyType(); + proxyHost = connectionSettings.getProxyHost(); + proxyPort = connectionSettings.getProxyPort(); + proxyUser = connectionSettings.getProxyUser(); + proxyPassword = connectionSettings.getProxyPassword(); + started = false; + } + + public XMPPConnection getXMPPConnection() { + return xmppConnection; + } + + public ConnectionItem getConnectionItem() { + return connectionItem; + } + + /** + * Resolve SRV record. + * + * @param fqdn + * @param defaultHost + * @param defaultPort + */ + private void srvResolve(final String fqdn, final String defaultHost, + final int defaultPort) { + final Record[] records = DNSManager.getInstance().fetchRecords(fqdn); + runOnUiThread(new Runnable() { + @Override + public void run() { + onSRVResolved(fqdn, defaultHost, defaultPort, records); + } + }); + } + + /** + * Called when srv records are resolved. + * + * @param fqdn + * @param defaultHost + * @param defaultPort + * @param records + */ + private void onSRVResolved(final String fqdn, final String defaultHost, + final int defaultPort, Record[] records) { + DNSManager.getInstance().onRecordsReceived(fqdn, records); + final Target target = DNSManager.getInstance().getCurrentTarget(fqdn); + if (target == null) { + LogManager.i(this, "Use defaults"); + runOnConnectionThread(new Runnable() { + @Override + public void run() { + if(proxyType == ProxyType.socks5) { + onReady(defaultHost, defaultPort); + } + else { + addressResolve(null, defaultHost, defaultPort, true); + } + } + }); + } else { + runOnConnectionThread(new Runnable() { + @Override + public void run() { + if(proxyType == ProxyType.socks5) { + onReady(target.getHost(), target.getPort()); + } + else { + addressResolve(fqdn, target.getHost(), target.getPort(), + true); + } + } + }); + } + } + + /** + * Resolves address to connect to. + * + * @param fqdn server name of subsequence SRV lookup. Should be + * null for custom host work flow. + * @param host + * @param port + * @param firstRequest + */ + private void addressResolve(final String fqdn, final String host, + final int port, final boolean firstRequest) { + LogManager.i(this, "Resolve " + host + ":" + port); + final InetAddress[] addresses = DNSManager.getInstance() + .fetchAddresses(host); + runOnUiThread(new Runnable() { + @Override + public void run() { + onAddressResolved(fqdn, host, port, firstRequest, addresses); + } + }); + } + + /** + * Called when address resolved are resolved. + * + * @param fqdn + * @param host + * @param port + * @param firstRequest + * @param addresses + */ + private void onAddressResolved(final String fqdn, final String host, + final int port, boolean firstRequest, final InetAddress[] addresses) { + DNSManager.getInstance().onAddressesReceived(host, addresses); + InetAddress address = DNSManager.getInstance().getNextAddress(host); + if (address != null) { + onReady(address, port); + return; + } + if (fqdn == null) { + if (firstRequest) { + onAddressResolved(null, host, port, false, addresses); + return; + } + } else { + DNSManager.getInstance().getNextTarget(fqdn); + if (DNSManager.getInstance().getCurrentTarget(fqdn) == null + && firstRequest) + DNSManager.getInstance().getNextTarget(fqdn); + final Target target = DNSManager.getInstance().getCurrentTarget( + fqdn); + if (target != null) { + runOnConnectionThread(new Runnable() { + @Override + public void run() { + addressResolve(fqdn, target.getHost(), + target.getPort(), false); + } + }); + return; + } + } + XMPPException exception = new XMPPException( + "There is no available address."); + LogManager.exception(this, exception); + connectionClosedOnError(exception); + } + + /** + * Called when configuration is ready and xmpp connection instance can be + * created. + * + * @param records + */ + private void onReady(final InetAddress address, final int port) { + LogManager.i(this, "Use " + address); + ProxyInfo proxy = proxyType.getProxyInfo(proxyHost, proxyPort, + proxyUser, proxyPassword); + ConnectionConfiguration connectionConfiguration = new ConnectionConfiguration( + address.getHostAddress(), port, serverName, proxy); + onReady(connectionConfiguration); + } + + private void onReady(final String host, final int port) { + LogManager.i(this, "Use remote DNS for " + host); + ProxyInfo proxy = proxyType.getProxyInfo(proxyHost, proxyPort, + proxyUser, proxyPassword); + ConnectionConfiguration connectionConfiguration = new ConnectionConfiguration( + host, port, serverName, proxy); + onReady(connectionConfiguration); + } + + private void onReady(ConnectionConfiguration connectionConfiguration) { + if (Build.VERSION.SDK_INT >= 14) { + connectionConfiguration.setTruststoreType("AndroidCAStore"); + connectionConfiguration.setTruststorePassword(null); + connectionConfiguration.setTruststorePath(null); + } else { + connectionConfiguration.setTruststoreType("BKS"); + connectionConfiguration + .setTruststorePath(ConnectionManager.TRUST_STORE_PATH); + } + // Disable smack`s reconnection. + connectionConfiguration.setReconnectionAllowed(false); + // We will send custom presence. + connectionConfiguration.setSendPresence(false); + // We use own roster management. + connectionConfiguration.setRosterLoadedAtLogin(false); + + if (SettingsManager.securityCheckCertificate()) { + connectionConfiguration.setExpiredCertificatesCheckEnabled(true); + connectionConfiguration.setNotMatchingDomainCheckEnabled(true); + connectionConfiguration.setSelfSignedCertificateEnabled(false); + connectionConfiguration.setVerifyChainEnabled(true); + connectionConfiguration.setVerifyRootCAEnabled(true); + connectionConfiguration.setCertificateListener(CertificateManager + .getInstance().createCertificateListener(connectionItem)); + } else { + connectionConfiguration.setExpiredCertificatesCheckEnabled(false); + connectionConfiguration.setNotMatchingDomainCheckEnabled(false); + connectionConfiguration.setSelfSignedCertificateEnabled(true); + connectionConfiguration.setVerifyChainEnabled(false); + connectionConfiguration.setVerifyRootCAEnabled(false); + } + + connectionConfiguration.setSASLAuthenticationEnabled(saslEnabled); + connectionConfiguration.setSecurityMode(tlsMode.getSecurityMode()); + connectionConfiguration.setCompressionEnabled(compression); + + xmppConnection = new XMPPConnection(connectionConfiguration); + xmppConnection.addPacketListener(this, ACCEPT_ALL); + xmppConnection.forceAddConnectionListener(this); + connectionItem.onSRVResolved(this); + final String password = OAuthManager.getInstance().getPassword( + protocol, token); + if (password != null) { + runOnConnectionThread(new Runnable() { + @Override + public void run() { + connect(password); + } + }); + } else { + runOnConnectionThread(new Runnable() { + @Override + public void run() { + passwordRequest(); + } + }); + } + } + + /** + * Request to renew password using OAuth. + */ + private void passwordRequest() { + final OAuthResult oAuthResult; + try { + oAuthResult = OAuthManager.getInstance().requestAccessToken( + protocol, token); + } catch (NetworkException e) { + throw new RuntimeException(e); + } + runOnUiThread(new Runnable() { + @Override + public void run() { + onPasswordReceived(oAuthResult); + } + }); + } + + /** + * Called when password has been renewed. + * + * @param oAuthResult + */ + private void onPasswordReceived(final OAuthResult oAuthResult) { + OAuthManager.getInstance().onAccessTokenReceived(oAuthResult); + if (oAuthResult == null) { + connectionItem.onAuthFailed(); + return; + } + connectionItem.onPasswordChanged(oAuthResult.getRefreshToken()); + final String password = oAuthResult.getAccessToken(); + runOnConnectionThread(new Runnable() { + @Override + public void run() { + connect(password); + } + }); + } + + /** + * Server requests us to see another host. + * + * @param target + */ + private void seeOtherHost(String target) { + Matcher matcher = ADDRESS_AND_PORT.matcher(target); + int defaultPort = 5222; + if (matcher.matches()) { + String value = matcher.group(2); + try { + defaultPort = Integer.valueOf(value); + target = matcher.group(1); + } catch (NumberFormatException e2) { + } + } + final String fqdn = target; + final int port = defaultPort; + // TODO: Check for the same address. + runOnUiThread(new Runnable() { + @Override + public void run() { + connectionItem.onSeeOtherHost(ConnectionThread.this, fqdn, + port, true); + } + }); + } + + /** + * Connect to the server. + * + * @param password + */ + private void connect(final String password) { + try { + xmppConnection.connect(); + } catch (XMPPException e) { + checkForCertificateError(e); + if (!checkForSeeOtherHost(e)) { + // There is no connection listeners yet, so we call onClose. + throw new RuntimeException(e); + } + } catch (IllegalStateException e) { + // There is no connection listeners yet, so we call onClose. + // connectionCreated() in other modules can fail. + throw new RuntimeException(e); + } + runOnUiThread(new Runnable() { + @Override + public void run() { + onConnected(password); + } + }); + } + + /** + * Check for certificate exception. + * + * @param e + */ + private void checkForCertificateError(Exception e) { + if (!(e instanceof XMPPException)) + return; + Throwable e1 = ((XMPPException) e).getWrappedThrowable(); + if (!(e1 instanceof SSLException)) + return; + Throwable e2 = e1.getCause(); + if (!(e2 instanceof CertificateException)) + return; + runOnUiThread(new Runnable() { + @Override + public void run() { + connectionItem.onInvalidCertificate(); + } + }); + } + + /** + * Check for see other host exception. + * + * @param e + * @return true if {@link StreamError.Type#seeOtherHost} has + * been found. + */ + private boolean checkForSeeOtherHost(Exception e) { + if (!(e instanceof XMPPException)) + return false; + StreamError streamError = ((XMPPException) e).getStreamError(); + if (streamError == null + || streamError.getType() != StreamError.Type.seeOtherHost) + return false; + String target = streamError.getBody(); + if (target == null || "".equals(target)) + return false; + LogManager.i(this, "See other host: " + target); + seeOtherHost(target); + return true; + } + + /** + * Connection to the server has been established. + * + * @param password + */ + private void onConnected(final String password) { + connectionItem.onConnected(this); + ConnectionManager.getInstance().onConnected(this); + if(registerNewAccount) { + runOnConnectionThread(new Runnable() { + @Override + public void run() { + registerAccount(password); + } + }); + } + else { + runOnConnectionThread(new Runnable() { + @Override + public void run() { + authorization(password); + } + }); + } + } + + /** + * Register new account. + * + * @param password + */ + private void registerAccount(final String password) { + try { + xmppConnection.getAccountManager().createAccount(login, password); + } + catch (XMPPException e) { + LogManager.exception(connectionItem, e); + connectionClosedOnError(e); + // Server will destroy connection, but we can speedup + // it. + xmppConnection.disconnect(); + return; + } + runOnUiThread(new Runnable() { + @Override + public void run() { + onAccountRegistered(password); + } + }); + } + + /** + * New account has been registerd on the server. + * + * @param password + */ + private void onAccountRegistered(final String password) { + LogManager.i(this, "Account registered"); + connectionItem.onAccountRegistered(this); + runOnConnectionThread(new Runnable() { + @Override + public void run() { + authorization(password); + } + }); + } + + /** + * Login. + * + * @param password + */ + private void authorization(String password) { + try { + xmppConnection.login(login, password, resource); + } catch (IllegalStateException e) { + // onClose must be called from reader thread. + return; + } catch (XMPPException e) { + LogManager.exception(connectionItem, e); + // SASLAuthentication#authenticate(String,String,String) + boolean SASLfailed = e.getMessage() != null + && e.getMessage().startsWith("SASL authentication ") + && !e.getMessage().endsWith("temporary-auth-failure"); + // NonSASLAuthentication#authenticate(String,String,String) + // Authentication request failed (doesn't supported), + // error was returned after negotiation or + // authentication failed. + boolean NonSASLfailed = e.getXMPPError() != null + && "Authentication failed.".equals(e.getMessage()); + if (SASLfailed || NonSASLfailed) { + // connectionClosed can be called before from reader thread, + // so don't check whether connection is managed. + Application.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + // Login failed. We don`t want to reconnect. + connectionItem.onAuthFailed(); + } + }); + connectionClosed(); + } else + connectionClosedOnError(e); + // Server will destroy connection, but we can speedup + // it. + xmppConnection.disconnect(); + return; + } + runOnUiThread(new Runnable() { + @Override + public void run() { + onAuthorized(); + } + }); + } + + /** + * Authorization passed. + */ + private void onAuthorized() { + connectionItem.onAuthorized(this); + ConnectionManager.getInstance().onAuthorized(this); + if (connectionItem instanceof AccountItem) + AccountManager.getInstance().removeAuthorizationError( + ((AccountItem) connectionItem).getAccount()); + shutdown(); + } + + @Override + public void connectionClosed() { + // Can be called on error, e.g. XMPPConnection#initConnection(). + runOnUiThread(new Runnable() { + @Override + public void run() { + connectionItem.onClose(ConnectionThread.this); + } + }); + } + + @Override + public void connectionClosedOnError(Exception e) { + checkForCertificateError(e); + if (checkForSeeOtherHost(e)) + return; + connectionClosed(); + } + + @Override + public void reconnectingIn(int seconds) { + } + + @Override + public void reconnectionSuccessful() { + } + + @Override + public void reconnectionFailed(Exception e) { + } + + @Override + public void processPacket(final Packet packet) { + Application.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + ConnectionManager.getInstance().processPacket( + ConnectionThread.this, packet); + } + }); + } + + /** + * Filter to accept all packets. + * + * @author alexander.ivanov + */ + static class AcceptAll implements PacketFilter { + @Override + public boolean accept(Packet packet) { + return true; + } + } + + /** + * Start connection. + *

+ * This function can be called only once. + * + * @param fqdn Fully Qualified Domain Names. + * @param port Preferred port. + * @param useSRVLookup Whether SRV lookup should be used. + */ + synchronized void start(final String fqdn, final int port, + final boolean useSRVLookup, + final boolean registerNewAccount) { + if (started) + throw new IllegalStateException(); + started = true; + this.registerNewAccount = registerNewAccount; + runOnConnectionThread(new Runnable() { + @Override + public void run() { + if (useSRVLookup) + srvResolve(fqdn, fqdn, port); + else { + if(proxyType == ProxyType.socks5) { + onReady(fqdn, port); + } + else { + addressResolve(null, fqdn, port, true); + } + } + } + }); + } + + /** + * Stop connection. + *

+ * {@link #start()} MUST BE CALLED FIRST. + */ + void shutdown() { + executorService.shutdownNow(); + } + + /** + * Submit task to be executed in connection thread. + * + * @param runnable + */ + private void runOnConnectionThread(final Runnable runnable) { + executorService.submit(new Runnable() { + @Override + public void run() { + if (!connectionItem.isManaged(ConnectionThread.this)) + return; + try { + runnable.run(); + } catch (RuntimeException e) { + LogManager.exception(connectionItem, e); + connectionClosedOnError(e); + } + } + }); + } + + /** + * Commit changes received from connection thread in UI thread. + * + * @param runnable + */ + private void runOnUiThread(final Runnable runnable) { + Application.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + if (!connectionItem.isManaged(ConnectionThread.this)) + return; + runnable.run(); + } + }); + } } diff --git a/app/src/main/java/com/xabber/android/data/connection/DNSManager.java b/app/src/main/java/com/xabber/android/data/connection/DNSManager.java index 08f01dcabb..9d64bef16b 100644 --- a/app/src/main/java/com/xabber/android/data/connection/DNSManager.java +++ b/app/src/main/java/com/xabber/android/data/connection/DNSManager.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -34,229 +34,224 @@ /** * Manage SRV records. - * + * * @author alexander.ivanov - * */ public class DNSManager { - private static final DNSManager instance; + private static final DNSManager instance; - static { - instance = new DNSManager(); - Application.getInstance().addManager(instance); - } + static { + instance = new DNSManager(); + Application.getInstance().addManager(instance); + } - public static DNSManager getInstance() { - return instance; - } + public static DNSManager getInstance() { + return instance; + } - /** - * Map with SRV records for each FQDN. - */ - private final Map srvs; + /** + * Map with SRV records for each FQDN. + */ + private final Map srvs; - /** - * Map with Addresses for each host. - */ - private final Map hosts; + /** + * Map with Addresses for each host. + */ + private final Map hosts; - /** - * Whether resolver update has been requested. - */ - private boolean resolverUpdateRequested; + /** + * Whether resolver update has been requested. + */ + private boolean resolverUpdateRequested; - /** - * Lock to update resolver only once per request. - */ - private final Object resolverUpdateLock; + /** + * Lock to update resolver only once per request. + */ + private final Object resolverUpdateLock; - private DNSManager() { - srvs = new HashMap(); - hosts = new HashMap(); - resolverUpdateLock = new Object(); - resolverUpdateRequested = true; - } + private DNSManager() { + srvs = new HashMap(); + hosts = new HashMap(); + resolverUpdateLock = new Object(); + resolverUpdateRequested = true; + } - /** - * Requests to update resolver before the next SRV resolving. - */ - public void requestResolverUpdate() { - resolverUpdateRequested = true; - } + /** + * Requests to update resolver before the next SRV resolving. + */ + public void requestResolverUpdate() { + resolverUpdateRequested = true; + } - /** - * Updates DNS server on demand. - */ - private void updateDNSServer() { - synchronized (resolverUpdateLock) { - if (!resolverUpdateRequested) - return; - resolverUpdateRequested = false; - ResolverConfig.refresh(); - ExtendedResolver resolver; - try { - resolver = new ExtendedResolver(); - } catch (UnknownHostException e) { - LogManager.exception(this, e); - return; - } - for (Resolver check : resolver.getResolvers()) - if (check instanceof SimpleResolver) { - LogManager.i(check, "Current timeout is " - + ((SimpleResolver) check).getTimeout()); - ((SimpleResolver) check).setTimeout(30); - LogManager.i(check, "new value is " - + ((SimpleResolver) check).getTimeout()); - } else { - LogManager.i(this, "Not simple resolver!!!?" + check); - } - synchronized (Lookup.class) { - Lookup.setDefaultResolver(resolver); - Lookup.setDefaultSearchPath(ResolverConfig.getCurrentConfig() - .searchPath()); - } - String message = "DNS servers:\n"; - if (ResolverConfig.getCurrentConfig().servers() == null) - message += ResolverConfig.getCurrentConfig().servers(); - else - for (String server : ResolverConfig.getCurrentConfig() - .servers()) - message += server + "\n"; - LogManager.i(this, message); - } - } + /** + * Updates DNS server on demand. + */ + private void updateDNSServer() { + synchronized (resolverUpdateLock) { + if (!resolverUpdateRequested) + return; + resolverUpdateRequested = false; + ResolverConfig.refresh(); + ExtendedResolver resolver; + try { + resolver = new ExtendedResolver(); + } catch (UnknownHostException e) { + LogManager.exception(this, e); + return; + } + for (Resolver check : resolver.getResolvers()) + if (check instanceof SimpleResolver) { + check.setTimeout(30); + } else { + LogManager.i(this, "Not simple resolver!!!?" + check); + } + synchronized (Lookup.class) { + Lookup.setDefaultResolver(resolver); + Lookup.setDefaultSearchPath(ResolverConfig.getCurrentConfig() + .searchPath()); + } + String message = "DNS servers:\n"; + if (ResolverConfig.getCurrentConfig().servers() == null) + message += ResolverConfig.getCurrentConfig().servers(); + else + for (String server : ResolverConfig.getCurrentConfig() + .servers()) + message += server + "\n"; + LogManager.i(this, message); + } + } - /** - * Requests records for the given resource name. - * - * @param name - * @return - */ - private Record[] getRecords(String name) { - try { - return new Lookup(name, Type.SRV).run(); - } catch (TextParseException e) { - } catch (NullPointerException e) { - } catch (ExceptionInInitializerError e) { - } - return null; - } + /** + * Requests records for the given resource name. + * + * @param name + * @return + */ + private Record[] getRecords(String name) { + try { + return new Lookup(name, Type.SRV).run(); + } catch (TextParseException e) { + } catch (NullPointerException e) { + } catch (ExceptionInInitializerError e) { + } + return null; + } - /** - * Requests SRV records for specified server. - * - * @param fqdn - * @return - */ - public Record[] fetchRecords(String fqdn) { - updateDNSServer(); - Record recs[] = getRecords("_xmpp-client._tcp." + fqdn); - if (recs != null) - return recs; - return getRecords("_jabber._tcp." + fqdn); - } + /** + * Requests SRV records for specified server. + * + * @param fqdn + * @return + */ + public Record[] fetchRecords(String fqdn) { + updateDNSServer(); + Record recs[] = getRecords("_xmpp-client._tcp." + fqdn); + if (recs != null) + return recs; + return getRecords("_jabber._tcp." + fqdn); + } - /** - * Updates information about server when {@link #fetchRecords(String)} has - * been completed. - * - * @param fqdn - * @param records - */ - public void onRecordsReceived(String fqdn, Record[] records) { - SRVContainer entity = srvs.get(fqdn); - if (entity == null) { - entity = new SRVContainer(); - srvs.put(fqdn, entity); - } - entity.update(records); - String message = "Update records for " + fqdn + ":\n"; - if (records == null) - message += records; - else - for (Record record : records) - message += record + "\n"; - LogManager.i(this, message); - } + /** + * Updates information about server when {@link #fetchRecords(String)} has + * been completed. + * + * @param fqdn + * @param records + */ + public void onRecordsReceived(String fqdn, Record[] records) { + SRVContainer entity = srvs.get(fqdn); + if (entity == null) { + entity = new SRVContainer(); + srvs.put(fqdn, entity); + } + entity.update(records); + String message = "Update records for " + fqdn + ":\n"; + if (records == null) + message += records; + else + for (Record record : records) + message += record + "\n"; + LogManager.i(this, message); + } - /** - * Requests addresses for specified target. - * - * @param target - * @return - */ - public InetAddress[] fetchAddresses(String host) { - updateDNSServer(); - try { - // TODO: AAAA support. - return Address.getAllByName(host); - } catch (UnknownHostException e) { - } - return null; - } + /** + * Requests addresses for specified target. + * + * @param target + * @return + */ + public InetAddress[] fetchAddresses(String host) { + updateDNSServer(); + try { + // TODO: AAAA support. + return Address.getAllByName(host); + } catch (UnknownHostException e) { + } + return null; + } - /** - * Updates information about servers when {@link #fetchAddresses(String)} - * has been completed. - * - * @param host - * @param addresses - */ - public void onAddressesReceived(String host, InetAddress[] addresses) { - Host entity = hosts.get(host); - if (entity == null) { - entity = new Host(); - hosts.put(host, entity); - } - entity.update(addresses); - String message = "Update address for " + host + ":\n"; - if (addresses == null) - message += addresses; - else - for (InetAddress record : addresses) - message += record.toString() + "\n"; - LogManager.i(this, message); - } + /** + * Updates information about servers when {@link #fetchAddresses(String)} + * has been completed. + * + * @param host + * @param addresses + */ + public void onAddressesReceived(String host, InetAddress[] addresses) { + Host entity = hosts.get(host); + if (entity == null) { + entity = new Host(); + hosts.put(host, entity); + } + entity.update(addresses); + String message = "Update address for " + host + ":\n"; + if (addresses == null) + message += addresses; + else + for (InetAddress record : addresses) + message += record.toString() + "\n"; + LogManager.i(this, message); + } - /** - * Returns current target to be used. - * - * @return null if there is no available targets. - */ - public Target getCurrentTarget(String fqdn) { - SRVContainer srv = srvs.get(fqdn); - if (srv == null) - return null; - return srv.getCurrent(); - } + /** + * Returns current target to be used. + * + * @return null if there is no available targets. + */ + public Target getCurrentTarget(String fqdn) { + SRVContainer srv = srvs.get(fqdn); + if (srv == null) + return null; + return srv.getCurrent(); + } - /** - * Returns first value from the pool. Make it as used. Reset pool if it is - * empty. - * - * @param server - * @return null if pool was empty. - */ - public Target getNextTarget(String server) { - SRVContainer srv = srvs.get(server); - if (srv == null) - return null; - return srv.getNext(); - } + /** + * Returns first value from the pool. Make it as used. Reset pool if it is + * empty. + * + * @param server + * @return null if pool was empty. + */ + public Target getNextTarget(String server) { + SRVContainer srv = srvs.get(server); + if (srv == null) + return null; + return srv.getNext(); + } - /** - * Returns first value from the pool. Make it as used. Reset pool if it is - * empty. - * - * @param host - * @return null if pool was empty. - */ - public InetAddress getNextAddress(String host) { - Host entity = hosts.get(host); - if (entity == null) - return null; - return entity.getNext(); - } + /** + * Returns first value from the pool. Make it as used. Reset pool if it is + * empty. + * + * @param host + * @return null if pool was empty. + */ + public InetAddress getNextAddress(String host) { + Host entity = hosts.get(host); + if (entity == null) + return null; + return entity.getNext(); + } } diff --git a/app/src/main/java/com/xabber/android/data/connection/Host.java b/app/src/main/java/com/xabber/android/data/connection/Host.java index c7f963fcc8..b3d6c3552b 100644 --- a/app/src/main/java/com/xabber/android/data/connection/Host.java +++ b/app/src/main/java/com/xabber/android/data/connection/Host.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,30 +20,29 @@ /** * Addresses holder for the host. - * + * * @author alexander.ivanov - * */ public class Host extends AbstractPool { - public Host() { - super(); - } + public Host() { + super(); + } - @Override - InetAddress convert(InetAddress value) { - return value; - } + @Override + InetAddress convert(InetAddress value) { + return value; + } - @Override - void update(List items) { - List exists = new ArrayList(pool); - exists.addAll(used); - for (InetAddress item : items) - if (!exists.remove(item)) - pool.add(item); - pool.removeAll(exists); - used.removeAll(exists); - } + @Override + void update(List items) { + List exists = new ArrayList(pool); + exists.addAll(used); + for (InetAddress item : items) + if (!exists.remove(item)) + pool.add(item); + pool.removeAll(exists); + used.removeAll(exists); + } } diff --git a/app/src/main/java/com/xabber/android/data/connection/NetworkManager.java b/app/src/main/java/com/xabber/android/data/connection/NetworkManager.java index 71c70958e4..d4bf6520fd 100644 --- a/app/src/main/java/com/xabber/android/data/connection/NetworkManager.java +++ b/app/src/main/java/com/xabber/android/data/connection/NetworkManager.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -33,195 +33,194 @@ /** * Manage network connectivity. - * + * * @author alexander.ivanov - * */ public class NetworkManager implements OnCloseListener, OnInitializedListener { - private final ConnectivityReceiver connectivityReceiver; - - private final ConnectivityManager connectivityManager; - - /** - * Type of last active network. - */ - private Integer type; - - /** - * Whether last active network was suspended. - */ - private boolean suspended; - - private final WifiLock wifiLock; - - private final WakeLock wakeLock; - - /** - * Current network state. - */ - private NetworkState state; - - private static final NetworkManager instance; - - static { - instance = new NetworkManager(Application.getInstance()); - Application.getInstance().addManager(instance); - } - - public static NetworkManager getInstance() { - return instance; - } - - private NetworkManager(Application application) { - connectivityReceiver = new ConnectivityReceiver(); - connectivityManager = (ConnectivityManager) application - .getSystemService(Context.CONNECTIVITY_SERVICE); - NetworkInfo active = connectivityManager.getActiveNetworkInfo(); - type = getType(active); - suspended = isSuspended(active); - - wifiLock = ((WifiManager) application - .getSystemService(Context.WIFI_SERVICE)).createWifiLock( - WifiManager.WIFI_MODE_FULL, "Xabber Wifi Lock"); - wifiLock.setReferenceCounted(false); - wakeLock = ((PowerManager) application - .getSystemService(Context.POWER_SERVICE)).newWakeLock( - PowerManager.PARTIAL_WAKE_LOCK, "Xabber Wake Lock"); - wakeLock.setReferenceCounted(false); - state = NetworkState.available; - } - - /** - * @param networkInfo - * @return Type of network. null if network is - * null or it is not connected and is not suspended. - */ - private Integer getType(NetworkInfo networkInfo) { - if (networkInfo != null - && (networkInfo.getState() == State.CONNECTED || networkInfo - .getState() == State.SUSPENDED)) - return networkInfo.getType(); - return null; - } - - /** - * @param networkInfo - * @return true if network is not null and is - * suspended. - */ - private boolean isSuspended(NetworkInfo networkInfo) { - return networkInfo != null && networkInfo.getState() == State.SUSPENDED; - } - - public NetworkState getState() { - return state; - } - - @Override - public void onInitialized() { - IntentFilter filter = new IntentFilter(); - filter.addAction(ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED); - filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); - Application.getInstance() - .registerReceiver(connectivityReceiver, filter); - onWakeLockSettingsChanged(); - onWifiLockSettingsChanged(); - } - - @Override - public void onClose() { - Application.getInstance().unregisterReceiver(connectivityReceiver); - } - - public void onNetworkChange(NetworkInfo networkInfo) { - NetworkInfo active = connectivityManager.getActiveNetworkInfo(); - LogManager.i(this, "Network: " + networkInfo + ", active: " + active); - Integer type; - boolean suspended; - if (active == null && this.type != null - && this.type == networkInfo.getType()) { - type = getType(networkInfo); - suspended = isSuspended(networkInfo); - } else { - type = getType(active); - suspended = isSuspended(active); - } - if (this.type == type) { - if (this.suspended == suspended) - LogManager.i(this, "State does not changed."); - else if (suspended) - onSuspend(); - else - onResume(); - } else { - if (suspended) { - type = null; - suspended = false; - } - if (type == null) - onUnavailable(); - else - onAvailable(type); - } - this.type = type; - this.suspended = suspended; - } - - /** - * New network is available. Start connection. - */ - private void onAvailable(int type) { - state = NetworkState.available; - LogManager.i(this, "Available"); - DNSManager.getInstance().requestResolverUpdate(); - if (type == ConnectivityManager.TYPE_WIFI) - ConnectionManager.getInstance().forceReconnect(); - else - ConnectionManager.getInstance().updateConnections(false); - } - - /** - * Network is temporary unavailable. - */ - private void onSuspend() { - state = NetworkState.suspended; - LogManager.i(this, "Suspend"); - // TODO: ConnectionManager.getInstance().pauseKeepAlive(); - } - - /** - * Network becomes available after suspend. - */ - private void onResume() { - state = NetworkState.available; - LogManager.i(this, "Resume"); - DNSManager.getInstance().requestResolverUpdate(); - ConnectionManager.getInstance().updateConnections(false); - // TODO: ConnectionManager.getInstance().forceKeepAlive(); - } - - /** - * Network is not available. Stop connections. - */ - private void onUnavailable() { - state = NetworkState.unavailable; - LogManager.i(this, "Unavailable"); - ConnectionManager.getInstance().updateConnections(false); - } - - public void onWifiLockSettingsChanged() { - if (SettingsManager.connectionWifiLock()) - wifiLock.acquire(); - else - wifiLock.release(); - } - - public void onWakeLockSettingsChanged() { - if (SettingsManager.connectionWakeLock()) - wakeLock.acquire(); - else - wakeLock.release(); - } + private final ConnectivityReceiver connectivityReceiver; + + private final ConnectivityManager connectivityManager; + + /** + * Type of last active network. + */ + private Integer type; + + /** + * Whether last active network was suspended. + */ + private boolean suspended; + + private final WifiLock wifiLock; + + private final WakeLock wakeLock; + + /** + * Current network state. + */ + private NetworkState state; + + private static final NetworkManager instance; + + static { + instance = new NetworkManager(Application.getInstance()); + Application.getInstance().addManager(instance); + } + + public static NetworkManager getInstance() { + return instance; + } + + private NetworkManager(Application application) { + connectivityReceiver = new ConnectivityReceiver(); + connectivityManager = (ConnectivityManager) application + .getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo active = connectivityManager.getActiveNetworkInfo(); + type = getType(active); + suspended = isSuspended(active); + + wifiLock = ((WifiManager) application + .getSystemService(Context.WIFI_SERVICE)).createWifiLock( + WifiManager.WIFI_MODE_FULL, "Xabber Wifi Lock"); + wifiLock.setReferenceCounted(false); + wakeLock = ((PowerManager) application + .getSystemService(Context.POWER_SERVICE)).newWakeLock( + PowerManager.PARTIAL_WAKE_LOCK, "Xabber Wake Lock"); + wakeLock.setReferenceCounted(false); + state = NetworkState.available; + } + + /** + * @param networkInfo + * @return Type of network. null if network is + * null or it is not connected and is not suspended. + */ + private Integer getType(NetworkInfo networkInfo) { + if (networkInfo != null + && (networkInfo.getState() == State.CONNECTED || networkInfo + .getState() == State.SUSPENDED)) + return networkInfo.getType(); + return null; + } + + /** + * @param networkInfo + * @return true if network is not null and is + * suspended. + */ + private boolean isSuspended(NetworkInfo networkInfo) { + return networkInfo != null && networkInfo.getState() == State.SUSPENDED; + } + + public NetworkState getState() { + return state; + } + + @Override + public void onInitialized() { + IntentFilter filter = new IntentFilter(); + filter.addAction(ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED); + filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); + Application.getInstance() + .registerReceiver(connectivityReceiver, filter); + onWakeLockSettingsChanged(); + onWifiLockSettingsChanged(); + } + + @Override + public void onClose() { + Application.getInstance().unregisterReceiver(connectivityReceiver); + } + + public void onNetworkChange(NetworkInfo networkInfo) { + NetworkInfo active = connectivityManager.getActiveNetworkInfo(); + LogManager.i(this, "Network: " + networkInfo + ", active: " + active); + Integer type; + boolean suspended; + if (active == null && this.type != null + && this.type == networkInfo.getType()) { + type = getType(networkInfo); + suspended = isSuspended(networkInfo); + } else { + type = getType(active); + suspended = isSuspended(active); + } + if (this.type == type) { + if (this.suspended == suspended) + LogManager.i(this, "State does not changed."); + else if (suspended) + onSuspend(); + else + onResume(); + } else { + if (suspended) { + type = null; + suspended = false; + } + if (type == null) + onUnavailable(); + else + onAvailable(type); + } + this.type = type; + this.suspended = suspended; + } + + /** + * New network is available. Start connection. + */ + private void onAvailable(int type) { + state = NetworkState.available; + LogManager.i(this, "Available"); + DNSManager.getInstance().requestResolverUpdate(); + if (type == ConnectivityManager.TYPE_WIFI) + ConnectionManager.getInstance().forceReconnect(); + else + ConnectionManager.getInstance().updateConnections(false); + } + + /** + * Network is temporary unavailable. + */ + private void onSuspend() { + state = NetworkState.suspended; + LogManager.i(this, "Suspend"); + // TODO: ConnectionManager.getInstance().pauseKeepAlive(); + } + + /** + * Network becomes available after suspend. + */ + private void onResume() { + state = NetworkState.available; + LogManager.i(this, "Resume"); + DNSManager.getInstance().requestResolverUpdate(); + ConnectionManager.getInstance().updateConnections(false); + // TODO: ConnectionManager.getInstance().forceKeepAlive(); + } + + /** + * Network is not available. Stop connections. + */ + private void onUnavailable() { + state = NetworkState.unavailable; + LogManager.i(this, "Unavailable"); + ConnectionManager.getInstance().updateConnections(false); + } + + public void onWifiLockSettingsChanged() { + if (SettingsManager.connectionWifiLock()) + wifiLock.acquire(); + else + wifiLock.release(); + } + + public void onWakeLockSettingsChanged() { + if (SettingsManager.connectionWakeLock()) + wakeLock.acquire(); + else + wakeLock.release(); + } } diff --git a/app/src/main/java/com/xabber/android/data/connection/NetworkState.java b/app/src/main/java/com/xabber/android/data/connection/NetworkState.java index b7b2d9c05e..6986452871 100644 --- a/app/src/main/java/com/xabber/android/data/connection/NetworkState.java +++ b/app/src/main/java/com/xabber/android/data/connection/NetworkState.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,16 +16,15 @@ /** * Network state. - * + * * @author alexander.ivanov - * */ public enum NetworkState { - available, + available, - suspended, + suspended, - unavailable; + unavailable } diff --git a/app/src/main/java/com/xabber/android/data/connection/OnAuthorizedListener.java b/app/src/main/java/com/xabber/android/data/connection/OnAuthorizedListener.java index 4ed58c3ee7..2a1a2d1fb1 100644 --- a/app/src/main/java/com/xabber/android/data/connection/OnAuthorizedListener.java +++ b/app/src/main/java/com/xabber/android/data/connection/OnAuthorizedListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,19 +18,18 @@ /** * Listener for authorization. - * + * * @author alexander.ivanov - * */ public interface OnAuthorizedListener extends BaseManagerInterface { - /** - * Authorization was complied. - * - * No one another other packets has been sent or received yet. - * - * @param connection - */ - void onAuthorized(ConnectionItem connection); + /** + * Authorization was complied. + *

+ * No one another other packets has been sent or received yet. + * + * @param connection + */ + void onAuthorized(ConnectionItem connection); } diff --git a/app/src/main/java/com/xabber/android/data/connection/OnConnectedListener.java b/app/src/main/java/com/xabber/android/data/connection/OnConnectedListener.java index 086ed4b393..770befe662 100644 --- a/app/src/main/java/com/xabber/android/data/connection/OnConnectedListener.java +++ b/app/src/main/java/com/xabber/android/data/connection/OnConnectedListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,17 +18,16 @@ /** * Listener for connection state change. - * + * * @author alexander.ivanov - * */ public interface OnConnectedListener extends BaseManagerInterface { - /** - * Connection with server was established. - * - * @param connection - */ - void onConnected(ConnectionItem connection); + /** + * Connection with server was established. + * + * @param connection + */ + void onConnected(ConnectionItem connection); } diff --git a/app/src/main/java/com/xabber/android/data/connection/OnConnectionListener.java b/app/src/main/java/com/xabber/android/data/connection/OnConnectionListener.java index b0b49f2839..425f7399ba 100644 --- a/app/src/main/java/com/xabber/android/data/connection/OnConnectionListener.java +++ b/app/src/main/java/com/xabber/android/data/connection/OnConnectionListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,17 +18,16 @@ /** * Listener for connection state change. - * + * * @author alexander.ivanov - * */ public interface OnConnectionListener extends BaseManagerInterface { - /** - * New {@link ConnectionThread} was created. - * - * @param connection - */ - void onConnection(ConnectionItem connection); + /** + * New {@link ConnectionThread} was created. + * + * @param connection + */ + void onConnection(ConnectionItem connection); } diff --git a/app/src/main/java/com/xabber/android/data/connection/OnDisconnectListener.java b/app/src/main/java/com/xabber/android/data/connection/OnDisconnectListener.java index c7421ce3c5..018cda8114 100644 --- a/app/src/main/java/com/xabber/android/data/connection/OnDisconnectListener.java +++ b/app/src/main/java/com/xabber/android/data/connection/OnDisconnectListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,17 +18,16 @@ /** * Listener for connection state change. - * + * * @author alexander.ivanov - * */ public interface OnDisconnectListener extends BaseManagerInterface { - /** - * Disconnection occur on some reason. - * - * @param connection - */ - void onDisconnect(ConnectionItem connection); + /** + * Disconnection occur on some reason. + * + * @param connection + */ + void onDisconnect(ConnectionItem connection); } diff --git a/app/src/main/java/com/xabber/android/data/connection/OnPacketListener.java b/app/src/main/java/com/xabber/android/data/connection/OnPacketListener.java index dd25a83e27..a8ffc65dc5 100644 --- a/app/src/main/java/com/xabber/android/data/connection/OnPacketListener.java +++ b/app/src/main/java/com/xabber/android/data/connection/OnPacketListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,19 +20,19 @@ /** * Listener for incoming packet. - * + * * @author alexander.ivanov */ public interface OnPacketListener extends BaseManagerInterface { - /** - * Process packet from connection. - * - * @param connection - * @param bareAddress - * @param packet - * @return - */ - void onPacket(ConnectionItem connection, String bareAddress, Packet packet); + /** + * Process packet from connection. + * + * @param connection + * @param bareAddress + * @param packet + * @return + */ + void onPacket(ConnectionItem connection, String bareAddress, Packet packet); } diff --git a/app/src/main/java/com/xabber/android/data/connection/OnResponseListener.java b/app/src/main/java/com/xabber/android/data/connection/OnResponseListener.java index aea19dbc51..7259458938 100644 --- a/app/src/main/java/com/xabber/android/data/connection/OnResponseListener.java +++ b/app/src/main/java/com/xabber/android/data/connection/OnResponseListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,18 +18,17 @@ /** * Listen for the response to the packet. - * + * * @author alexander.ivanov - * */ public interface OnResponseListener { - void onReceived(String account, String packetId, IQ iq); + void onReceived(String account, String packetId, IQ iq); - void onError(String account, String packetId, IQ iq); + void onError(String account, String packetId, IQ iq); - void onTimeout(String account, String packetId); + void onTimeout(String account, String packetId); - void onDisconnect(String account, String packetId); + void onDisconnect(String account, String packetId); } diff --git a/app/src/main/java/com/xabber/android/data/connection/PendingCertificate.java b/app/src/main/java/com/xabber/android/data/connection/PendingCertificate.java index abd54ffa63..9ad59a4dde 100644 --- a/app/src/main/java/com/xabber/android/data/connection/PendingCertificate.java +++ b/app/src/main/java/com/xabber/android/data/connection/PendingCertificate.java @@ -1,165 +1,165 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data.connection; -import java.security.Principal; -import java.security.cert.X509Certificate; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - import android.content.Intent; +import com.xabber.android.R; import com.xabber.android.data.Application; import com.xabber.android.data.notification.NotificationItem; import com.xabber.android.ui.CertificateConfirmation; import com.xabber.android.utils.StringUtils; -import com.xabber.androiddev.R; + +import java.security.Principal; +import java.security.cert.X509Certificate; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class PendingCertificate implements NotificationItem { - private static Pattern cnPattern = Pattern.compile("(?i)(cn=)([^,]*)"); - private static Pattern ouPattern = Pattern.compile("(?i)(ou=)([^,]*)"); - private static Pattern oPattern = Pattern.compile("(?i)(o=)([^,]*)"); - - private final String server; - - private final X509Certificate x509Certificate; - - private final CertificateInvalidReason reason; - - private final String fingerprint; - - public PendingCertificate(String server, CertificateInvalidReason reason, - X509Certificate x509Certificate, String fingerprint) { - super(); - this.server = server; - this.reason = reason; - this.x509Certificate = x509Certificate; - this.fingerprint = fingerprint; - } - - public String getServer() { - return server; - } - - public String getFingerprint() { - return fingerprint; - } - - public String issuedOn() { - return StringUtils.getDateTimeText(x509Certificate.getNotBefore()); - } - - public String expiresOn() { - return StringUtils.getDateTimeText(x509Certificate.getNotAfter()); - } - - private String getValue(Pattern pattern, Principal principal) { - String name = principal.getName(); - Matcher matcher = pattern.matcher(name); - if (matcher.find()) - return matcher.group(2); - return ""; - } - - public String getIssuerCommonName() { - return getValue(cnPattern, x509Certificate.getSubjectDN()); - } - - public String getIssuerOrganization() { - return getValue(oPattern, x509Certificate.getSubjectDN()); - } - - public String getIssuerOrganizationlUnit() { - return getValue(ouPattern, x509Certificate.getSubjectDN()); - } - - public String getSubjectCommonName() { - return getValue(cnPattern, x509Certificate.getIssuerDN()); - } - - public String getSubjectOrganization() { - return getValue(oPattern, x509Certificate.getIssuerDN()); - } - - public String getSubjectOrganizationlUnit() { - return getValue(ouPattern, x509Certificate.getIssuerDN()); - } - - public String getSerialNumber() { - return x509Certificate.getSerialNumber().toString(); - } - - public CertificateInvalidReason getReason() { - return reason; - } - - X509Certificate getX509Certificate() { - return x509Certificate; - } - - @Override - public Intent getIntent() { - return CertificateConfirmation.createIntent(Application.getInstance(), - getFingerprint(), reason); - } - - @Override - public String getTitle() { - return Application.getInstance() - .getString(R.string.INVALID_CERTIFICATE); - } - - @Override - public String getText() { - return getServer(); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result - + ((fingerprint == null) ? 0 : fingerprint.hashCode()); - result = prime * result + ((reason == null) ? 0 : reason.hashCode()); - result = prime * result + ((server == null) ? 0 : server.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - PendingCertificate other = (PendingCertificate) obj; - if (fingerprint == null) { - if (other.fingerprint != null) - return false; - } else if (!fingerprint.equals(other.fingerprint)) - return false; - if (reason != other.reason) - return false; - if (server == null) { - if (other.server != null) - return false; - } else if (!server.equals(other.server)) - return false; - return true; - } + private static Pattern cnPattern = Pattern.compile("(?i)(cn=)([^,]*)"); + private static Pattern ouPattern = Pattern.compile("(?i)(ou=)([^,]*)"); + private static Pattern oPattern = Pattern.compile("(?i)(o=)([^,]*)"); + + private final String server; + + private final X509Certificate x509Certificate; + + private final CertificateInvalidReason reason; + + private final String fingerprint; + + public PendingCertificate(String server, CertificateInvalidReason reason, + X509Certificate x509Certificate, String fingerprint) { + super(); + this.server = server; + this.reason = reason; + this.x509Certificate = x509Certificate; + this.fingerprint = fingerprint; + } + + public String getServer() { + return server; + } + + public String getFingerprint() { + return fingerprint; + } + + public String issuedOn() { + return StringUtils.getDateTimeText(x509Certificate.getNotBefore()); + } + + public String expiresOn() { + return StringUtils.getDateTimeText(x509Certificate.getNotAfter()); + } + + private String getValue(Pattern pattern, Principal principal) { + String name = principal.getName(); + Matcher matcher = pattern.matcher(name); + if (matcher.find()) + return matcher.group(2); + return ""; + } + + public String getIssuerCommonName() { + return getValue(cnPattern, x509Certificate.getSubjectDN()); + } + + public String getIssuerOrganization() { + return getValue(oPattern, x509Certificate.getSubjectDN()); + } + + public String getIssuerOrganizationlUnit() { + return getValue(ouPattern, x509Certificate.getSubjectDN()); + } + + public String getSubjectCommonName() { + return getValue(cnPattern, x509Certificate.getIssuerDN()); + } + + public String getSubjectOrganization() { + return getValue(oPattern, x509Certificate.getIssuerDN()); + } + + public String getSubjectOrganizationlUnit() { + return getValue(ouPattern, x509Certificate.getIssuerDN()); + } + + public String getSerialNumber() { + return x509Certificate.getSerialNumber().toString(); + } + + public CertificateInvalidReason getReason() { + return reason; + } + + X509Certificate getX509Certificate() { + return x509Certificate; + } + + @Override + public Intent getIntent() { + return CertificateConfirmation.createIntent(Application.getInstance(), + getFingerprint(), reason); + } + + @Override + public String getTitle() { + return Application.getInstance() + .getString(R.string.INVALID_CERTIFICATE); + } + + @Override + public String getText() { + return getServer(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((fingerprint == null) ? 0 : fingerprint.hashCode()); + result = prime * result + ((reason == null) ? 0 : reason.hashCode()); + result = prime * result + ((server == null) ? 0 : server.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + PendingCertificate other = (PendingCertificate) obj; + if (fingerprint == null) { + if (other.fingerprint != null) + return false; + } else if (!fingerprint.equals(other.fingerprint)) + return false; + if (reason != other.reason) + return false; + if (server == null) { + if (other.server != null) + return false; + } else if (!server.equals(other.server)) + return false; + return true; + } } diff --git a/app/src/main/java/com/xabber/android/data/connection/ProxyType.java b/app/src/main/java/com/xabber/android/data/connection/ProxyType.java index a2f4edce1f..828fcccb2e 100644 --- a/app/src/main/java/com/xabber/android/data/connection/ProxyType.java +++ b/app/src/main/java/com/xabber/android/data/connection/ProxyType.java @@ -4,37 +4,37 @@ public enum ProxyType { - none, - - http, - - socks4, - - socks5, - - orbot; - - public ProxyInfo getProxyInfo(String proxyHost, int proxyPort, - String proxyUser, String proxyPassword) { - ProxyInfo.ProxyType proxyType; - if (this == none) - proxyType = ProxyInfo.ProxyType.NONE; - else if (this == http) - proxyType = ProxyInfo.ProxyType.HTTP; - else if (this == socks4) - proxyType = ProxyInfo.ProxyType.SOCKS4; - else if (this == socks5) - proxyType = ProxyInfo.ProxyType.SOCKS5; - else if (this == orbot) { - proxyType = ProxyInfo.ProxyType.SOCKS5; - proxyHost = "localhost"; - proxyPort = 9050; - proxyUser = ""; - proxyPassword = ""; - } else - throw new IllegalStateException(); - return new ProxyInfo(proxyType, proxyHost, proxyPort, proxyUser, - proxyPassword); - } + none, + + http, + + socks4, + + socks5, + + orbot; + + public ProxyInfo getProxyInfo(String proxyHost, int proxyPort, + String proxyUser, String proxyPassword) { + ProxyInfo.ProxyType proxyType; + if (this == none) + proxyType = ProxyInfo.ProxyType.NONE; + else if (this == http) + proxyType = ProxyInfo.ProxyType.HTTP; + else if (this == socks4) + proxyType = ProxyInfo.ProxyType.SOCKS4; + else if (this == socks5) + proxyType = ProxyInfo.ProxyType.SOCKS5; + else if (this == orbot) { + proxyType = ProxyInfo.ProxyType.SOCKS5; + proxyHost = "localhost"; + proxyPort = 9050; + proxyUser = ""; + proxyPassword = ""; + } else + throw new IllegalStateException(); + return new ProxyInfo(proxyType, proxyHost, proxyPort, proxyUser, + proxyPassword); + } } diff --git a/app/src/main/java/com/xabber/android/data/connection/ReconnectionManager.java b/app/src/main/java/com/xabber/android/data/connection/ReconnectionManager.java index 37d88b2f18..ee0ffadb15 100644 --- a/app/src/main/java/com/xabber/android/data/connection/ReconnectionManager.java +++ b/app/src/main/java/com/xabber/android/data/connection/ReconnectionManager.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -24,104 +24,103 @@ import com.xabber.android.data.account.OnAccountRemovedListener; public class ReconnectionManager implements OnConnectionListener, - OnConnectedListener, OnAccountRemovedListener, OnTimerListener { - - /** - * Intervals in seconds to be used for attempt to reconnect. First value - * will be used on first attempt. Next value will be used if reconnection - * fails. Last value will be used if there is no more values in array. - */ - private final static int RECONNECT_AFTER[] = new int[] { 2, 10, 30, 60 }; - - /** - * Managed connections. - */ - private final HashMap connections; - - private final static ReconnectionManager instance; - - static { - instance = new ReconnectionManager(Application.getInstance()); - Application.getInstance().addManager(instance); - } - - public static ReconnectionManager getInstance() { - return instance; - } - - private ReconnectionManager(Application application) { - connections = new HashMap(); - } - - @Override - public void onTimer() { - for (Entry entry : connections - .entrySet()) { - ReconnectionInfo reconnectionInfo = entry.getValue(); - ConnectionItem connectionItem = entry.getKey(); - if (connectionItem.getState() == ConnectionState.waiting) { - int reconnectAfter; - if (reconnectionInfo.reconnectAttempts < RECONNECT_AFTER.length) - reconnectAfter = RECONNECT_AFTER[reconnectionInfo.reconnectAttempts]; - else - reconnectAfter = RECONNECT_AFTER[RECONNECT_AFTER.length - 1]; - if (reconnectionInfo.reconnectCounter >= reconnectAfter) { - reconnectionInfo.reconnectCounter = 0; - reconnectionInfo.reconnectAttempts += 1; - connectionItem.updateConnection(false); - if (connectionItem instanceof AccountItem) - AccountManager.getInstance().onAccountChanged( - ((AccountItem) connectionItem).getAccount()); - } else { - reconnectionInfo.reconnectCounter += 1; - } - } else { - reconnectionInfo.reconnectCounter = 0; - } - } - } - - @Override - public void onConnection(ConnectionItem connection) { - ReconnectionInfo info = connections.get(connection); - if (info == null) { - info = new ReconnectionInfo(); - connections.put(connection, info); - } - info.reconnectAttempts += 1; - info.reconnectCounter = 0; - } - - @Override - public void onConnected(ConnectionItem connection) { - ReconnectionInfo info = connections.get(connection); - info.reconnectAttempts = 0; - info.reconnectCounter = 0; - } - - @Override - public void onAccountRemoved(AccountItem accountItem) { - connections.remove(accountItem); - } - - /** - * Information about reconnection attempts. - * - * @author alexander.ivanov - * - */ - private static class ReconnectionInfo { - - /** - * Number of attempts to reconnect without success. - */ - int reconnectAttempts = 0; - - /** - * Number of seconds passed from last reconnection. - */ - int reconnectCounter = 0; - - } + OnConnectedListener, OnAccountRemovedListener, OnTimerListener { + + /** + * Intervals in seconds to be used for attempt to reconnect. First value + * will be used on first attempt. Next value will be used if reconnection + * fails. Last value will be used if there is no more values in array. + */ + private final static int RECONNECT_AFTER[] = new int[]{2, 10, 30, 60}; + + /** + * Managed connections. + */ + private final HashMap connections; + + private final static ReconnectionManager instance; + + static { + instance = new ReconnectionManager(Application.getInstance()); + Application.getInstance().addManager(instance); + } + + public static ReconnectionManager getInstance() { + return instance; + } + + private ReconnectionManager(Application application) { + connections = new HashMap(); + } + + @Override + public void onTimer() { + for (Entry entry : connections + .entrySet()) { + ReconnectionInfo reconnectionInfo = entry.getValue(); + ConnectionItem connectionItem = entry.getKey(); + if (connectionItem.getState() == ConnectionState.waiting) { + int reconnectAfter; + if (reconnectionInfo.reconnectAttempts < RECONNECT_AFTER.length) + reconnectAfter = RECONNECT_AFTER[reconnectionInfo.reconnectAttempts]; + else + reconnectAfter = RECONNECT_AFTER[RECONNECT_AFTER.length - 1]; + if (reconnectionInfo.reconnectCounter >= reconnectAfter) { + reconnectionInfo.reconnectCounter = 0; + reconnectionInfo.reconnectAttempts += 1; + connectionItem.updateConnection(false); + if (connectionItem instanceof AccountItem) + AccountManager.getInstance().onAccountChanged( + ((AccountItem) connectionItem).getAccount()); + } else { + reconnectionInfo.reconnectCounter += 1; + } + } else { + reconnectionInfo.reconnectCounter = 0; + } + } + } + + @Override + public void onConnection(ConnectionItem connection) { + ReconnectionInfo info = connections.get(connection); + if (info == null) { + info = new ReconnectionInfo(); + connections.put(connection, info); + } + info.reconnectAttempts += 1; + info.reconnectCounter = 0; + } + + @Override + public void onConnected(ConnectionItem connection) { + ReconnectionInfo info = connections.get(connection); + info.reconnectAttempts = 0; + info.reconnectCounter = 0; + } + + @Override + public void onAccountRemoved(AccountItem accountItem) { + connections.remove(accountItem); + } + + /** + * Information about reconnection attempts. + * + * @author alexander.ivanov + */ + private static class ReconnectionInfo { + + /** + * Number of attempts to reconnect without success. + */ + int reconnectAttempts = 0; + + /** + * Number of seconds passed from last reconnection. + */ + int reconnectCounter = 0; + + } } diff --git a/app/src/main/java/com/xabber/android/data/connection/RequestHolder.java b/app/src/main/java/com/xabber/android/data/connection/RequestHolder.java index 953eb96e2e..9b1b0c0be5 100644 --- a/app/src/main/java/com/xabber/android/data/connection/RequestHolder.java +++ b/app/src/main/java/com/xabber/android/data/connection/RequestHolder.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,29 +18,28 @@ /** * Holder for listener to be notified about packet delivery. - * + * * @author alexander.ivanov - * */ public class RequestHolder { - private final long timeout; + private final long timeout; - private final OnResponseListener listener; + private final OnResponseListener listener; - public RequestHolder(OnResponseListener listener) { - super(); - this.timeout = new Date().getTime() - + ConnectionManager.PACKET_REPLY_TIMEOUT; - this.listener = listener; - } + public RequestHolder(OnResponseListener listener) { + super(); + this.timeout = new Date().getTime() + + ConnectionManager.PACKET_REPLY_TIMEOUT; + this.listener = listener; + } - public boolean isExpired(long now) { - return now > timeout; - } + public boolean isExpired(long now) { + return now > timeout; + } - public OnResponseListener getListener() { - return listener; - } + public OnResponseListener getListener() { + return listener; + } } diff --git a/app/src/main/java/com/xabber/android/data/connection/SRVContainer.java b/app/src/main/java/com/xabber/android/data/connection/SRVContainer.java index f3c7b1823d..8ccd07b766 100644 --- a/app/src/main/java/com/xabber/android/data/connection/SRVContainer.java +++ b/app/src/main/java/com/xabber/android/data/connection/SRVContainer.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -25,73 +25,72 @@ /** * Holder for SRV records for the same server. - * + * * @author alexander.ivanov - * */ class SRVContainer extends AbstractPool { - private final Random random; + private final Random random; - SRVContainer() { - super(); - random = new Random(); - } + SRVContainer() { + super(); + random = new Random(); + } - @Override - Target convert(Record value) { - if (value instanceof SRVRecord) - return new Target((SRVRecord) value); - return null; - } + @Override + Target convert(Record value) { + if (value instanceof SRVRecord) + return new Target((SRVRecord) value); + return null; + } - /** - * @param items - * @return Whether passed targets differs from previous one. - */ - private boolean hasChanges(Collection items) { - Collection exists = new ArrayList(pool); - exists.addAll(used); - for (Target target : items) - if (!exists.remove(target)) - return true; - return !exists.isEmpty(); - } + /** + * @param items + * @return Whether passed targets differs from previous one. + */ + private boolean hasChanges(Collection items) { + Collection exists = new ArrayList(pool); + exists.addAll(used); + for (Target target : items) + if (!exists.remove(target)) + return true; + return !exists.isEmpty(); + } - @Override - void update(List items) { - if (!hasChanges(items) && !pool.isEmpty()) - return; - pool.clear(); - used.clear(); - Collections.sort(items); - int[] values = new int[items.size()]; - while (!items.isEmpty()) { - int sum = 0; - int index = 0; - int priority = items.get(0).getPriority(); - while (index < items.size()) { - Target target = items.get(index); - if (priority != target.getPriority()) - break; - sum += target.getWeight(); - values[index] = sum; - index += 1; - } - int value = random.nextInt(sum + 1); - for (index = 0; index < items.size(); index++) - if (values[index] >= value) - break; - Target selected = items.remove(index); - pool.add(selected); - } - } + @Override + void update(List items) { + if (!hasChanges(items) && !pool.isEmpty()) + return; + pool.clear(); + used.clear(); + Collections.sort(items); + int[] values = new int[items.size()]; + while (!items.isEmpty()) { + int sum = 0; + int index = 0; + int priority = items.get(0).getPriority(); + while (index < items.size()) { + Target target = items.get(index); + if (priority != target.getPriority()) + break; + sum += target.getWeight(); + values[index] = sum; + index += 1; + } + int value = random.nextInt(sum + 1); + for (index = 0; index < items.size(); index++) + if (values[index] >= value) + break; + Target selected = items.remove(index); + pool.add(selected); + } + } - /** - * @return Current first target or null if pool is empty. - */ - synchronized public Target getCurrent() { - return pool.peek(); - } + /** + * @return Current first target or null if pool is empty. + */ + synchronized public Target getCurrent() { + return pool.peek(); + } } diff --git a/app/src/main/java/com/xabber/android/data/connection/TLSMode.java b/app/src/main/java/com/xabber/android/data/connection/TLSMode.java index d09604e70d..07d5f54784 100644 --- a/app/src/main/java/com/xabber/android/data/connection/TLSMode.java +++ b/app/src/main/java/com/xabber/android/data/connection/TLSMode.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,37 +18,36 @@ /** * Supported security mode. - * + * * @author alexander.ivanov - * */ public enum TLSMode { - /** - * Security via TLS encryption is used whenever it's available. - */ - enabled, + /** + * Security via TLS encryption is used whenever it's available. + */ + enabled, - /** - * Security via TLS encryption is required in order to connect. - */ - required, + /** + * Security via TLS encryption is required in order to connect. + */ + required, - /** - * Security via old SSL based encryption is enabled. If the server does not - * handle legacy-SSL, the connection to the server will fail. - */ - legacy; + /** + * Security via old SSL based encryption is enabled. If the server does not + * handle legacy-SSL, the connection to the server will fail. + */ + legacy; - SecurityMode getSecurityMode() { - if (this == enabled) - return SecurityMode.enabled; - else if (this == required) - return SecurityMode.required; - else if (this == legacy) - return SecurityMode.legacy; - else - throw new IllegalStateException(); - } + SecurityMode getSecurityMode() { + if (this == enabled) + return SecurityMode.enabled; + else if (this == required) + return SecurityMode.required; + else if (this == legacy) + return SecurityMode.legacy; + else + throw new IllegalStateException(); + } } diff --git a/app/src/main/java/com/xabber/android/data/connection/Target.java b/app/src/main/java/com/xabber/android/data/connection/Target.java index 55b45cd9cc..9c98d5c072 100644 --- a/app/src/main/java/com/xabber/android/data/connection/Target.java +++ b/app/src/main/java/com/xabber/android/data/connection/Target.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,86 +18,83 @@ /** * Single target and information about its addresses. - * + * * @author alexander.ivanov - * */ public class Target implements Comparable { - private final String host; - private final int port; - private final int priority; - private final int weight; + private final String host; + private final int port; + private final int priority; + private final int weight; - public Target(SRVRecord srvRecord) { - super(); - this.host = srvRecord.getTarget().toString(); - this.port = srvRecord.getPort(); - this.priority = srvRecord.getPriority(); - this.weight = Math.max(0, srvRecord.getWeight()); - } + public Target(SRVRecord srvRecord) { + super(); + this.host = srvRecord.getTarget().toString(); + this.port = srvRecord.getPort(); + this.priority = srvRecord.getPriority(); + this.weight = Math.max(0, srvRecord.getWeight()); + } - public String getHost() { - return host; - } + public String getHost() { + return host; + } - public int getPort() { - return port; - } + public int getPort() { + return port; + } - int getPriority() { - return priority; - } + int getPriority() { + return priority; + } - int getWeight() { - return weight; - } + int getWeight() { + return weight; + } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((host == null) ? 0 : host.hashCode()); - result = prime * result + port; - result = prime * result + priority; - result = prime * result + weight; - return result; - } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((host == null) ? 0 : host.hashCode()); + result = prime * result + port; + result = prime * result + priority; + result = prime * result + weight; + return result; + } - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Target other = (Target) obj; - if (host == null) { - if (other.host != null) - return false; - } else if (!host.equals(other.host)) - return false; - if (port != other.port) - return false; - if (priority != other.priority) - return false; - if (weight != other.weight) - return false; - return true; - } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Target other = (Target) obj; + if (host == null) { + if (other.host != null) + return false; + } else if (!host.equals(other.host)) + return false; + if (port != other.port) + return false; + if (priority != other.priority) + return false; + return weight == other.weight; + } - @Override - public int compareTo(Target another) { - int result = priority - another.priority; - if (result == 0) - return another.weight - weight; - return result; - } + @Override + public int compareTo(Target another) { + int result = priority - another.priority; + if (result == 0) + return another.weight - weight; + return result; + } - @Override - public String toString() { - return priority + " " + weight + " " + port + " " + host; - } + @Override + public String toString() { + return priority + " " + weight + " " + port + " " + host; + } } diff --git a/app/src/main/java/com/xabber/android/data/entity/AbstractAccountTable.java b/app/src/main/java/com/xabber/android/data/entity/AbstractAccountTable.java index 52307cff78..f006ddff7f 100644 --- a/app/src/main/java/com/xabber/android/data/entity/AbstractAccountTable.java +++ b/app/src/main/java/com/xabber/android/data/entity/AbstractAccountTable.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -24,31 +24,30 @@ /** * Table with account related information. - * + * * @author alexander.ivanov - * */ public abstract class AbstractAccountTable extends AbstractTable { - public static interface Fields extends BaseColumns { + public interface Fields extends BaseColumns { - public static final String ACCOUNT = "account"; + String ACCOUNT = "account"; - } + } - /** - * Remove records with specified account. - * - * @param account - */ - public void removeAccount(String account) { - SQLiteDatabase db = DatabaseManager.getInstance().getWritableDatabase(); - db.delete(getTableName(), Fields.ACCOUNT + " = ?", - new String[] { account }); - } + /** + * Remove records with specified account. + * + * @param account + */ + public void removeAccount(String account) { + SQLiteDatabase db = DatabaseManager.getInstance().getWritableDatabase(); + db.delete(getTableName(), Fields.ACCOUNT + " = ?", + new String[]{account}); + } - public static String getAccount(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.ACCOUNT)); - } + public static String getAccount(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.ACCOUNT)); + } } diff --git a/app/src/main/java/com/xabber/android/data/entity/AbstractEntityTable.java b/app/src/main/java/com/xabber/android/data/entity/AbstractEntityTable.java index 1812ca9f73..56595f9b31 100644 --- a/app/src/main/java/com/xabber/android/data/entity/AbstractEntityTable.java +++ b/app/src/main/java/com/xabber/android/data/entity/AbstractEntityTable.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,19 +18,19 @@ /** * Table with entity related information. - * + * * @author alexander.ivanov */ public abstract class AbstractEntityTable extends AbstractAccountTable { - public static interface Fields extends AbstractAccountTable.Fields { + public interface Fields extends AbstractAccountTable.Fields { - public static final String USER = "user"; + String USER = "user"; - } + } - public static String getUser(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.USER)); - } + public static String getUser(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.USER)); + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/data/entity/AccountRelated.java b/app/src/main/java/com/xabber/android/data/entity/AccountRelated.java index 2a29bf61c8..1eaa88ebc9 100644 --- a/app/src/main/java/com/xabber/android/data/entity/AccountRelated.java +++ b/app/src/main/java/com/xabber/android/data/entity/AccountRelated.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,44 +16,43 @@ /** * Object with relation to the account. - * + * * @author alexander.ivanov - * */ public abstract class AccountRelated { - protected final String account; + protected final String account; - public AccountRelated(String account) { - super(); - this.account = account; - } + public AccountRelated(String account) { + super(); + this.account = account; + } - public String getAccount() { - return account; - } + public String getAccount() { + return account; + } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((account == null) ? 0 : account.hashCode()); - return result; - } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((account == null) ? 0 : account.hashCode()); + return result; + } - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - AccountRelated other = (AccountRelated) obj; - if (account == null) { - if (other.account != null) - return false; - } else if (!account.equals(other.account)) - return false; - return true; - } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + AccountRelated other = (AccountRelated) obj; + if (account == null) { + if (other.account != null) + return false; + } else if (!account.equals(other.account)) + return false; + return true; + } } diff --git a/app/src/main/java/com/xabber/android/data/entity/BaseEntity.java b/app/src/main/java/com/xabber/android/data/entity/BaseEntity.java index 28da6b6011..a477600df6 100644 --- a/app/src/main/java/com/xabber/android/data/entity/BaseEntity.java +++ b/app/src/main/java/com/xabber/android/data/entity/BaseEntity.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,99 +16,99 @@ /** * Object with account and user fields. - * + * * @author alexander.ivanov */ public class BaseEntity extends AccountRelated implements - Comparable { + Comparable { - protected final String user; + protected final String user; - public BaseEntity(String account, String user) { - super(account); - this.user = user; - } + public BaseEntity(String account, String user) { + super(account); + this.user = user; + } - public BaseEntity(BaseEntity baseEntity) { - this(baseEntity.account, baseEntity.user); - } + public BaseEntity(BaseEntity baseEntity) { + this(baseEntity.account, baseEntity.user); + } - public String getUser() { - return user; - } + public String getUser() { + return user; + } - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((user == null) ? 0 : user.hashCode()); - return result; - } + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + ((user == null) ? 0 : user.hashCode()); + return result; + } - public boolean equals(String account, String user) { - if (this.account == null) { - if (account != null) - return false; - } else { - if (!this.account.equals(account)) - return false; - } - if (this.user == null) { - if (user != null) - return false; - } else { - if (!this.user.equals(user)) - return false; - } - return true; - } + public boolean equals(String account, String user) { + if (this.account == null) { + if (account != null) + return false; + } else { + if (!this.account.equals(account)) + return false; + } + if (this.user == null) { + if (user != null) + return false; + } else { + if (!this.user.equals(user)) + return false; + } + return true; + } - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (!super.equals(obj)) - return false; - BaseEntity other = (BaseEntity) obj; - if (user == null) { - if (other.user != null) - return false; - } else if (!user.equals(other.user)) - return false; - return true; - } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + BaseEntity other = (BaseEntity) obj; + if (user == null) { + if (other.user != null) + return false; + } else if (!user.equals(other.user)) + return false; + return true; + } - @Override - public int compareTo(BaseEntity another) { - if (account == null) { - if (another.account != null) - return -1; - } else { - if (another.account == null) - return 1; - else { - int result = account.compareTo(another.account); - if (result != 0) - return result; - } - } - if (user == null) { - if (another.user != null) - return -1; - } else { - if (another.user == null) - return 1; - else { - int result = user.compareTo(another.user); - if (result != 0) - return result; - } - } - return 0; - } + @Override + public int compareTo(BaseEntity another) { + if (account == null) { + if (another.account != null) + return -1; + } else { + if (another.account == null) + return 1; + else { + int result = account.compareTo(another.account); + if (result != 0) + return result; + } + } + if (user == null) { + if (another.user != null) + return -1; + } else { + if (another.user == null) + return 1; + else { + int result = user.compareTo(another.user); + if (result != 0) + return result; + } + } + return 0; + } - @Override - public String toString() { - return account + ":" + user; - } + @Override + public String toString() { + return account + ":" + user; + } } diff --git a/app/src/main/java/com/xabber/android/data/entity/NestedMap.java b/app/src/main/java/com/xabber/android/data/entity/NestedMap.java index d732dd0756..b99677ba99 100644 --- a/app/src/main/java/com/xabber/android/data/entity/NestedMap.java +++ b/app/src/main/java/com/xabber/android/data/entity/NestedMap.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -23,306 +23,304 @@ /** * Map of map with string value as keys for both maps. - * - * @author alexander.ivanov - * + * * @param + * @author alexander.ivanov */ public class NestedMap implements Iterable> { - private final Map> map; - - public NestedMap() { - map = new HashMap>(); - } - - /** - * @param first - * @param second - * @return null if there is no such first or second level. - */ - public T get(String first, String second) { - Map nested = map.get(first); - if (nested == null) - return null; - return nested.get(second); - } - - /** - * Puts value. Nested map will be created if necessary. - * - * @param first - * @param second - * @param value - */ - synchronized public void put(String first, String second, T value) { - Map nested = map.get(first); - if (nested == null) { - nested = new HashMap(); - map.put(first, nested); - } - nested.put(second, value); - } - - /** - * Removes value. Nested map will be removed if necessary. - * - * @param first - * @param second - */ - synchronized public T remove(String first, String second) { - Map nested = map.get(first); - if (nested == null) - return null; - T value = nested.remove(second); - if (nested.isEmpty()) - map.remove(first); - return value; - } - - /** - * Removes all information associated with first level. - * - * @param first - */ - synchronized public void clear(String first) { - map.remove(first); - } - - /** - * Removes all information. - */ - synchronized public void clear() { - map.clear(); - } - - /** - * @return Whether there is no values. - */ - synchronized public boolean isEmpty() { - return map.isEmpty(); - } - - /** - * Returns an {@link Iterator} for the elements in this object. - * - * Iterators are designed to be used by only one thread at a time. - * - * @return - */ - @Override - public Iterator> iterator() { - return new EntryIterator(); - } - - /** - * Returns nested map. - * - * @param first - * @return empty map if there is no such first level. - */ - public Map getNested(String first) { - Map nested = map.get(first); - if (nested == null) - return Collections.emptyMap(); - return Collections.unmodifiableMap(nested); - } - - /** - * Collection with values. - * - * ONLY {@link Collection#iterator()} FUNCTION IS SUPPORTED. - * - * @return - */ - public Collection values() { - return new Values(); - } - - /** - * Adds all elements from another {@link NestedMap}. - * - * @param nestedMap - */ - public void addAll(NestedMap nestedMap) { - for (NestedMap.Entry entry : nestedMap) - put(entry.getFirst(), entry.getSecond(), entry.getValue()); - } - - /** - * Entry stored in {@link NestedMap}. - * - * @author alexander.ivanov - * - * @param - */ - public static class Entry { - - private final String first; - private final String second; - private final T value; - - public Entry(String first, String second, T value) { - super(); - this.first = first; - this.second = second; - this.value = value; - } - - public String getFirst() { - return first; - } - - public String getSecond() { - return second; - } - - public T getValue() { - return value; - } - - } - - private class EntryIterator implements Iterator> { - - private final Iterator>> firstIterator; - - private java.util.Map.Entry> nested; - - private Iterator> secondIterator; - - private EntryIterator() { - firstIterator = map.entrySet().iterator(); - nested = null; - secondIterator = null; - } - - @Override - public boolean hasNext() { - if (secondIterator != null && secondIterator.hasNext()) - return true; - while (firstIterator.hasNext()) { - nested = firstIterator.next(); - secondIterator = nested.getValue().entrySet().iterator(); - if (secondIterator.hasNext()) { - return true; - } - } - return false; - } - - @Override - public Entry next() throws NoSuchElementException { - if (!hasNext()) - throw new NoSuchElementException(); - java.util.Map.Entry entry = secondIterator.next(); - return new Entry(nested.getKey(), entry.getKey(), - entry.getValue()); - } - - @Override - public void remove() throws IllegalStateException { - if (secondIterator == null) - throw new IllegalStateException(); - secondIterator.remove(); - if (nested.getValue().isEmpty()) - firstIterator.remove(); - } - - } - - private class Values implements Collection { - - @Override - public boolean add(T object) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean addAll(Collection arg0) { - throw new UnsupportedOperationException(); - } - - @Override - public void clear() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean contains(Object object) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean containsAll(Collection arg0) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isEmpty() { - return iterator().hasNext(); - } - - @Override - public Iterator iterator() { - return new ValuesIterator(); - } - - @Override - public boolean remove(Object object) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean removeAll(Collection arg0) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean retainAll(Collection arg0) { - throw new UnsupportedOperationException(); - } - - @Override - public int size() { - throw new UnsupportedOperationException(); - } - - @Override - public Object[] toArray() { - throw new UnsupportedOperationException(); - } - - @Override - public Type[] toArray(Type[] array) { - throw new UnsupportedOperationException(); - } - - private class ValuesIterator implements Iterator { - - private final Iterator> iterator; - - private ValuesIterator() { - this.iterator = NestedMap.this.iterator(); - } - - @Override - public boolean hasNext() { - return iterator.hasNext(); - } - - @Override - public T next() { - return iterator.next().getValue(); - } - - @Override - public void remove() { - iterator.remove(); - } - - } - - } + private final Map> map; + + public NestedMap() { + map = new HashMap>(); + } + + /** + * @param first + * @param second + * @return null if there is no such first or second level. + */ + public T get(String first, String second) { + Map nested = map.get(first); + if (nested == null) + return null; + return nested.get(second); + } + + /** + * Puts value. Nested map will be created if necessary. + * + * @param first + * @param second + * @param value + */ + synchronized public void put(String first, String second, T value) { + Map nested = map.get(first); + if (nested == null) { + nested = new HashMap(); + map.put(first, nested); + } + nested.put(second, value); + } + + /** + * Removes value. Nested map will be removed if necessary. + * + * @param first + * @param second + */ + synchronized public T remove(String first, String second) { + Map nested = map.get(first); + if (nested == null) + return null; + T value = nested.remove(second); + if (nested.isEmpty()) + map.remove(first); + return value; + } + + /** + * Removes all information associated with first level. + * + * @param first + */ + synchronized public void clear(String first) { + map.remove(first); + } + + /** + * Removes all information. + */ + synchronized public void clear() { + map.clear(); + } + + /** + * @return Whether there is no values. + */ + synchronized public boolean isEmpty() { + return map.isEmpty(); + } + + /** + * Returns an {@link Iterator} for the elements in this object. + *

+ * Iterators are designed to be used by only one thread at a time. + * + * @return + */ + @Override + public Iterator> iterator() { + return new EntryIterator(); + } + + /** + * Returns nested map. + * + * @param first + * @return empty map if there is no such first level. + */ + public Map getNested(String first) { + Map nested = map.get(first); + if (nested == null) + return Collections.emptyMap(); + return Collections.unmodifiableMap(nested); + } + + /** + * Collection with values. + *

+ * ONLY {@link Collection#iterator()} FUNCTION IS SUPPORTED. + * + * @return + */ + public Collection values() { + return new Values(); + } + + /** + * Adds all elements from another {@link NestedMap}. + * + * @param nestedMap + */ + public void addAll(NestedMap nestedMap) { + for (NestedMap.Entry entry : nestedMap) + put(entry.getFirst(), entry.getSecond(), entry.getValue()); + } + + /** + * Entry stored in {@link NestedMap}. + * + * @param + * @author alexander.ivanov + */ + public static class Entry { + + private final String first; + private final String second; + private final T value; + + public Entry(String first, String second, T value) { + super(); + this.first = first; + this.second = second; + this.value = value; + } + + public String getFirst() { + return first; + } + + public String getSecond() { + return second; + } + + public T getValue() { + return value; + } + + } + + private class EntryIterator implements Iterator> { + + private final Iterator>> firstIterator; + + private java.util.Map.Entry> nested; + + private Iterator> secondIterator; + + private EntryIterator() { + firstIterator = map.entrySet().iterator(); + nested = null; + secondIterator = null; + } + + @Override + public boolean hasNext() { + if (secondIterator != null && secondIterator.hasNext()) + return true; + while (firstIterator.hasNext()) { + nested = firstIterator.next(); + secondIterator = nested.getValue().entrySet().iterator(); + if (secondIterator.hasNext()) { + return true; + } + } + return false; + } + + @Override + public Entry next() throws NoSuchElementException { + if (!hasNext()) + throw new NoSuchElementException(); + java.util.Map.Entry entry = secondIterator.next(); + return new Entry(nested.getKey(), entry.getKey(), + entry.getValue()); + } + + @Override + public void remove() throws IllegalStateException { + if (secondIterator == null) + throw new IllegalStateException(); + secondIterator.remove(); + if (nested.getValue().isEmpty()) + firstIterator.remove(); + } + + } + + private class Values implements Collection { + + @Override + public boolean add(T object) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(Collection arg0) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean contains(Object object) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean containsAll(Collection arg0) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isEmpty() { + return iterator().hasNext(); + } + + @Override + public Iterator iterator() { + return new ValuesIterator(); + } + + @Override + public boolean remove(Object object) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean removeAll(Collection arg0) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean retainAll(Collection arg0) { + throw new UnsupportedOperationException(); + } + + @Override + public int size() { + throw new UnsupportedOperationException(); + } + + @Override + public Object[] toArray() { + throw new UnsupportedOperationException(); + } + + @Override + public Type[] toArray(Type[] array) { + throw new UnsupportedOperationException(); + } + + private class ValuesIterator implements Iterator { + + private final Iterator> iterator; + + private ValuesIterator() { + this.iterator = NestedMap.this.iterator(); + } + + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public T next() { + return iterator.next().getValue(); + } + + @Override + public void remove() { + iterator.remove(); + } + + } + + } } diff --git a/app/src/main/java/com/xabber/android/data/entity/NestedNestedMaps.java b/app/src/main/java/com/xabber/android/data/entity/NestedNestedMaps.java index 92a42e8624..b9b859846e 100644 --- a/app/src/main/java/com/xabber/android/data/entity/NestedNestedMaps.java +++ b/app/src/main/java/com/xabber/android/data/entity/NestedNestedMaps.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -19,41 +19,40 @@ /** * Three level nested map. - * + *

* Required inner maps will be created if necessary. - * - * @author alexander.ivanov - * + * * @param * @param + * @author alexander.ivanov */ public class NestedNestedMaps extends NestedMap> { - public Value get(String first, String second, Key third) { - Map map = get(first, second); - if (map == null) - return null; - return map.get(third); - } + public Value get(String first, String second, Key third) { + Map map = get(first, second); + if (map == null) + return null; + return map.get(third); + } - synchronized public void put(String first, String second, Key third, - Value value) { - Map map = get(first, second); - if (map == null) { - map = new HashMap(); - put(first, second, map); - } - map.put(third, value); - } + synchronized public void put(String first, String second, Key third, + Value value) { + Map map = get(first, second); + if (map == null) { + map = new HashMap(); + put(first, second, map); + } + map.put(third, value); + } - synchronized public Value remove(String first, String second, Key third) { - Map map = get(first, second); - if (map == null) - return null; - Value value = map.remove(third); - if (map.isEmpty()) - remove(first, second); - return value; - } + synchronized public Value remove(String first, String second, Key third) { + Map map = get(first, second); + if (map == null) + return null; + Value value = map.remove(third); + if (map.isEmpty()) + remove(first, second); + return value; + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/archive/ArchivePreference.java b/app/src/main/java/com/xabber/android/data/extension/archive/ArchivePreference.java index 1061053fed..29c0b78745 100644 --- a/app/src/main/java/com/xabber/android/data/extension/archive/ArchivePreference.java +++ b/app/src/main/java/com/xabber/android/data/extension/archive/ArchivePreference.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -19,28 +19,27 @@ /** * Archive preference pair. - * + * * @author alexander.ivanov - * */ public class ArchivePreference { - private final OtrMode otrMode; + private final OtrMode otrMode; - private final SaveMode saveMode; + private final SaveMode saveMode; - public ArchivePreference(OtrMode otrMode, SaveMode saveMode) { - super(); - this.otrMode = otrMode; - this.saveMode = saveMode; - } + public ArchivePreference(OtrMode otrMode, SaveMode saveMode) { + super(); + this.otrMode = otrMode; + this.saveMode = saveMode; + } - public OtrMode getOtrMode() { - return otrMode; - } + public OtrMode getOtrMode() { + return otrMode; + } - public SaveMode getSaveMode() { - return saveMode; - } + public SaveMode getSaveMode() { + return saveMode; + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/archive/AvailableArchiveRequest.java b/app/src/main/java/com/xabber/android/data/extension/archive/AvailableArchiveRequest.java index b31121e872..e2e0079f55 100644 --- a/app/src/main/java/com/xabber/android/data/extension/archive/AvailableArchiveRequest.java +++ b/app/src/main/java/com/xabber/android/data/extension/archive/AvailableArchiveRequest.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,35 +16,35 @@ import android.content.Intent; +import com.xabber.android.R; import com.xabber.android.data.Application; import com.xabber.android.data.account.AccountManager; import com.xabber.android.data.entity.AccountRelated; import com.xabber.android.data.notification.AccountNotificationItem; import com.xabber.android.ui.ArchiveRequest; -import com.xabber.androiddev.R; public class AvailableArchiveRequest extends AccountRelated implements - AccountNotificationItem { - - public AvailableArchiveRequest(String account) { - super(account); - } - - @Override - public Intent getIntent() { - return ArchiveRequest.createIntent( - Application.getInstance(), account); - } - - @Override - public String getTitle() { - return Application.getInstance().getString( - R.string.archive_available_request_title); - } - - @Override - public String getText() { - return AccountManager.getInstance().getVerboseName(account); - } + AccountNotificationItem { + + public AvailableArchiveRequest(String account) { + super(account); + } + + @Override + public Intent getIntent() { + return ArchiveRequest.createIntent( + Application.getInstance(), account); + } + + @Override + public String getTitle() { + return Application.getInstance().getString( + R.string.archive_available_request_title); + } + + @Override + public String getText() { + return AccountManager.getInstance().getVerboseName(account); + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/archive/ChatStorage.java b/app/src/main/java/com/xabber/android/data/extension/archive/ChatStorage.java index d26512a792..417b932b9a 100644 --- a/app/src/main/java/com/xabber/android/data/extension/archive/ChatStorage.java +++ b/app/src/main/java/com/xabber/android/data/extension/archive/ChatStorage.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -32,101 +32,100 @@ /** * Collect messages for the chat collection received from the message archive. - * + * * @author alexander.ivanov - * */ public class ChatStorage { - /** - * Whether received changes has been applied. - */ - private boolean applied; - - /** - * Whether all messages have been received. - */ - private boolean received; - - /** - * Last received chat version. - */ - private Integer version; - - /** - * Accumulated timestamp. - */ - private Date timestamp; - - /** - * Received messages. - */ - private final Collection items; - - public ChatStorage(Date timestamp) { - super(); - applied = false; - received = false; - version = null; - items = new ArrayList(); - this.timestamp = timestamp; - } - - public boolean isReceived() { - return received; - } - - public void onItemsReceived(Integer version) { - this.version = version; - received = true; - } - - public boolean hasVersion(Integer version) { - return this.version != null && version != null - && this.version.equals(version); - } - - public Collection getItems() { - return Collections.unmodifiableCollection(items); - } - - public void addItem(AbstractChat abstractChat, Chat chat, - AbstractMessage message, long offset) { - boolean incoming = message instanceof From; - if (message.getUtc() == null) - timestamp = new Date(timestamp.getTime() + message.getSecs() * 1000); - else - timestamp = message.getUtc(); - String body = message.getBody(); - net.java.otr4j.io.messages.AbstractMessage otrMessage; - try { - otrMessage = SerializationUtils.toMessage(body); - } catch (IOException e) { - return; - } - if (otrMessage != null) { - if (otrMessage.messageType != net.java.otr4j.io.messages.AbstractMessage.MESSAGE_PLAINTEXT) - return; - body = ((PlainTextMessage) otrMessage).cleanText; - } - MessageItem messageItem = new MessageItem(abstractChat, - chat.getStartString(), - Jid.getResource(chat.getWith()), body, null, - new Date(timestamp.getTime() - offset), null, incoming, true, - true, false, true, false, false); - items.add(messageItem); - } - - public boolean isApplied() { - return applied; - } - - /** - * Received messages has been added to the history and can be cleared. - */ - public void onApplied() { - items.clear(); - applied = true; - } + /** + * Whether received changes has been applied. + */ + private boolean applied; + + /** + * Whether all messages have been received. + */ + private boolean received; + + /** + * Last received chat version. + */ + private Integer version; + + /** + * Accumulated timestamp. + */ + private Date timestamp; + + /** + * Received messages. + */ + private final Collection items; + + public ChatStorage(Date timestamp) { + super(); + applied = false; + received = false; + version = null; + items = new ArrayList(); + this.timestamp = timestamp; + } + + public boolean isReceived() { + return received; + } + + public void onItemsReceived(Integer version) { + this.version = version; + received = true; + } + + public boolean hasVersion(Integer version) { + return this.version != null && version != null + && this.version.equals(version); + } + + public Collection getItems() { + return Collections.unmodifiableCollection(items); + } + + public void addItem(AbstractChat abstractChat, Chat chat, + AbstractMessage message, long offset) { + boolean incoming = message instanceof From; + if (message.getUtc() == null) + timestamp = new Date(timestamp.getTime() + message.getSecs() * 1000); + else + timestamp = message.getUtc(); + String body = message.getBody(); + net.java.otr4j.io.messages.AbstractMessage otrMessage; + try { + otrMessage = SerializationUtils.toMessage(body); + } catch (IOException e) { + return; + } + if (otrMessage != null) { + if (otrMessage.messageType != net.java.otr4j.io.messages.AbstractMessage.MESSAGE_PLAINTEXT) + return; + body = ((PlainTextMessage) otrMessage).cleanText; + } + MessageItem messageItem = new MessageItem(abstractChat, + chat.getStartString(), + Jid.getResource(chat.getWith()), body, null, + new Date(timestamp.getTime() - offset), null, incoming, true, + true, false, true, false, false); + items.add(messageItem); + } + + public boolean isApplied() { + return applied; + } + + /** + * Received messages has been added to the history and can be cleared. + */ + public void onApplied() { + items.clear(); + applied = true; + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/archive/HeaderSequence.java b/app/src/main/java/com/xabber/android/data/extension/archive/HeaderSequence.java index 6f4e29d825..8d87f7e07f 100644 --- a/app/src/main/java/com/xabber/android/data/extension/archive/HeaderSequence.java +++ b/app/src/main/java/com/xabber/android/data/extension/archive/HeaderSequence.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -23,99 +23,98 @@ /** * Store received sequence of the chat collection headers. - * + * * @author alexander.ivanov - * */ public class HeaderSequence { - /** - * Whether sequence or inner chat request is in progress. - */ - private boolean inProgress; - - /** - * Whether all chats has been received. - */ - private boolean received; - - /** - * List of chats to be populated. - */ - private final Queue headers; - - /** - * Number of received chat headers. - */ - private int headerCount; - - /** - * First received chat used for backward pagination. - */ - private String next; - - public HeaderSequence() { - headers = new LinkedList(); - reset(); - setInProgress(false); - } - - void reset() { - received = false; - headers.clear(); - headerCount = 0; - next = ""; - } - - public boolean isInProgress() { - return inProgress; - } - - void setInProgress(boolean inProgress) { - this.inProgress = inProgress; - } - - public boolean isHeadersReceived() { - return received; - } - - public void onHeadersReceived() { - received = true; - } - - public void addHeaders(Collection headers) { - ArrayList backward = new ArrayList(); - for (CollectionHeader header : headers) - backward.add(0, header); - this.headers.addAll(backward); - headerCount += headers.size(); - } - - public int getHeaderCount() { - return headerCount; - } - - /** - * @return Returns and removes first chat header from the query. - * null if there is no more items. - */ - public CollectionHeader pollHeader() { - return headers.poll(); - } - - /** - * @return Returns, but doesn't remove first chat header from the query. - * null if there is no more items. - */ - public CollectionHeader peekHeader() { - return headers.peek(); - } - - public String getNext() { - return next; - } - - public void setNext(String next) { - this.next = next; - } + /** + * Whether sequence or inner chat request is in progress. + */ + private boolean inProgress; + + /** + * Whether all chats has been received. + */ + private boolean received; + + /** + * List of chats to be populated. + */ + private final Queue headers; + + /** + * Number of received chat headers. + */ + private int headerCount; + + /** + * First received chat used for backward pagination. + */ + private String next; + + public HeaderSequence() { + headers = new LinkedList(); + reset(); + setInProgress(false); + } + + void reset() { + received = false; + headers.clear(); + headerCount = 0; + next = ""; + } + + public boolean isInProgress() { + return inProgress; + } + + void setInProgress(boolean inProgress) { + this.inProgress = inProgress; + } + + public boolean isHeadersReceived() { + return received; + } + + public void onHeadersReceived() { + received = true; + } + + public void addHeaders(Collection headers) { + ArrayList backward = new ArrayList(); + for (CollectionHeader header : headers) + backward.add(0, header); + this.headers.addAll(backward); + headerCount += headers.size(); + } + + public int getHeaderCount() { + return headerCount; + } + + /** + * @return Returns and removes first chat header from the query. + * null if there is no more items. + */ + public CollectionHeader pollHeader() { + return headers.poll(); + } + + /** + * @return Returns, but doesn't remove first chat header from the query. + * null if there is no more items. + */ + public CollectionHeader peekHeader() { + return headers.peek(); + } + + public String getNext() { + return next; + } + + public void setNext(String next) { + this.next = next; + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/archive/HistoryStorage.java b/app/src/main/java/com/xabber/android/data/extension/archive/HistoryStorage.java index fbfc8ff352..9649066fc7 100644 --- a/app/src/main/java/com/xabber/android/data/extension/archive/HistoryStorage.java +++ b/app/src/main/java/com/xabber/android/data/extension/archive/HistoryStorage.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,107 +16,106 @@ /** * Store history for the user while receiving from the message archive. - * + * * @author alexander.ivanov - * */ public class HistoryStorage extends HeaderSequence { - /** - * Packet ID of the list request. - */ - private String packetId; + /** + * Packet ID of the list request. + */ + private String packetId; - /** - * Number of new received messages. - */ - private int receivedNew; + /** + * Number of new received messages. + */ + private int receivedNew; - /** - * Number of incoming received messages. - */ - private int receivedIncoming; + /** + * Number of incoming received messages. + */ + private int receivedIncoming; - /** - * Number of new messages to be received. - */ - private Integer requestedNew; + /** + * Number of new messages to be received. + */ + private Integer requestedNew; - /** - * Number of incoming messages to be received. - */ - private Integer requestedIncoming; + /** + * Number of incoming messages to be received. + */ + private Integer requestedIncoming; - public HistoryStorage() { - super(); - onSuccess(); - } + public HistoryStorage() { + super(); + onSuccess(); + } - public boolean hasPacketId(String packetId) { - return this.packetId != null && this.packetId.equals(packetId); - } + public boolean hasPacketId(String packetId) { + return this.packetId != null && this.packetId.equals(packetId); + } - public void setPacketId(String packetId) { - this.packetId = packetId; - } + public void setPacketId(String packetId) { + this.packetId = packetId; + } - /** - * @param receivedNew - * @param receivedIncoming - * @return Whether there is enough messages received. - */ - public boolean enoughMessages(int receivedNew, int receivedIncoming) { - this.receivedNew += receivedNew; - this.receivedIncoming += receivedIncoming; - return this.receivedNew >= requestedNew - && this.receivedIncoming >= requestedIncoming; - } + /** + * @param receivedNew + * @param receivedIncoming + * @return Whether there is enough messages received. + */ + public boolean enoughMessages(int receivedNew, int receivedIncoming) { + this.receivedNew += receivedNew; + this.receivedIncoming += receivedIncoming; + return this.receivedNew >= requestedNew + && this.receivedIncoming >= requestedIncoming; + } - /** - * Request has been started. - * - * @param requestedNew - * @param requestedIncoming - */ - public void onRequest(int requestedNew, int requestedIncoming) { - this.requestedNew = requestedNew; - this.requestedIncoming = requestedIncoming; - onResume(); - } + /** + * Request has been started. + * + * @param requestedNew + * @param requestedIncoming + */ + public void onRequest(int requestedNew, int requestedIncoming) { + this.requestedNew = requestedNew; + this.requestedIncoming = requestedIncoming; + onResume(); + } - /** - * @return Whether next history request should be sent. - */ - public boolean onResume() { - packetId = null; - if (requestedNew <= 0 && requestedIncoming <= 0) - return false; - super.setInProgress(true); - return true; - } + /** + * @return Whether next history request should be sent. + */ + public boolean onResume() { + packetId = null; + if (requestedNew <= 0 && requestedIncoming <= 0) + return false; + super.setInProgress(true); + return true; + } - /** - * Set number of requested messages at least as specified. - * - * @param requestedNew - * @param requestedIncoming - */ - public void setRequestedCountAtLeast(int requestedNew, int requestedIncoming) { - this.requestedNew = Math.max(this.requestedNew, requestedNew); - this.requestedIncoming = Math.max(this.requestedIncoming, - requestedIncoming); - } + /** + * Set number of requested messages at least as specified. + * + * @param requestedNew + * @param requestedIncoming + */ + public void setRequestedCountAtLeast(int requestedNew, int requestedIncoming) { + this.requestedNew = Math.max(this.requestedNew, requestedNew); + this.requestedIncoming = Math.max(this.requestedIncoming, + requestedIncoming); + } - /** - * History portion request has been completed. - */ - public void onSuccess() { - super.setInProgress(false); - packetId = null; - receivedNew = 0; - receivedIncoming = 0; - requestedNew = 0; - requestedIncoming = 0; - } + /** + * History portion request has been completed. + */ + public void onSuccess() { + super.setInProgress(false); + packetId = null; + receivedNew = 0; + receivedIncoming = 0; + requestedNew = 0; + requestedIncoming = 0; + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/archive/MatchMode.java b/app/src/main/java/com/xabber/android/data/extension/archive/MatchMode.java index 69b5092c02..905502d7b6 100644 --- a/app/src/main/java/com/xabber/android/data/extension/archive/MatchMode.java +++ b/app/src/main/java/com/xabber/android/data/extension/archive/MatchMode.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,18 +16,17 @@ /** * Matching mode for message archive preferences. - * + *

* http://xmpp.org/extensions/xep-0136.html#impl-jidmatch - * + * * @author alexander.ivanov - * */ public enum MatchMode { - domain, + domain, - bare, + bare, - exect; + exect } diff --git a/app/src/main/java/com/xabber/android/data/extension/archive/MessageArchiveManager.java b/app/src/main/java/com/xabber/android/data/extension/archive/MessageArchiveManager.java index 1e8f661497..736f3d3a89 100644 --- a/app/src/main/java/com/xabber/android/data/extension/archive/MessageArchiveManager.java +++ b/app/src/main/java/com/xabber/android/data/extension/archive/MessageArchiveManager.java @@ -1,29 +1,20 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data.extension.archive; -import java.util.Collection; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; - -import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smack.packet.IQ.Type; -import org.jivesoftware.smack.packet.Packet; - +import com.xabber.android.R; import com.xabber.android.data.Application; import com.xabber.android.data.LogManager; import com.xabber.android.data.NetworkException; @@ -49,7 +40,6 @@ import com.xabber.android.data.message.MessageManager; import com.xabber.android.data.notification.BaseAccountNotificationProvider; import com.xabber.android.data.notification.NotificationManager; -import com.xabber.androiddev.R; import com.xabber.xmpp.address.Jid; import com.xabber.xmpp.archive.AbstractMessage; import com.xabber.xmpp.archive.Auto; @@ -68,874 +58,872 @@ import com.xabber.xmpp.archive.SessionRemove; import com.xabber.xmpp.rsm.Set; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.IQ.Type; +import org.jivesoftware.smack.packet.Packet; + +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + /** * Manage server side archive. Replicate it with memory storage. - * + *

* Terminology used in docstrings: - * + *

* Chat - jid (domain or bare or full) specified in with * attribute of the chat. - * + *

* Tag - string representation of the start attribute of the * chat. - * + * * @author alexander.ivanov - * */ public class MessageArchiveManager implements OnPacketListener, - OnTimeReceivedListener, OnAccountAddedListener, - OnAccountRemovedListener, OnLoadListener, - OnAccountArchiveModeChangedListener, OnDisconnectListener { - - private static final Integer SESSION_TIMEOUT = 7 * 24 * 60 * 60; - - private static final int RSM_MAX = 20; - - private static final String FEATURE_ARCH = "urn:xmpp:archive"; - private static final String FEATURE_PREF = "urn:xmpp:archive:pref"; - private static final String FEATURE_MANAGE = "urn:xmpp:archive:manage"; - - /** - * Custom auto setting per account. - */ - private final Map saves; - - /** - * Default settings for each account. - */ - private final Map defaults; - - /** - * Per user settings for each account. - */ - private final Map>> items; - - /** - * Settings for session in each account. - */ - private final NestedMap sessionSaves; - - /** - * Contains whether chat modification has been requested for the given - * packet id in the accounts. - */ - private final NestedMap modificationRequests; - - /** - * Store information about modifications received from the server. - */ - private final Map modificationStorages; - - /** - * Server side timestamp when connection has been established. - */ - private final Map connected; - - /** - * Store current history request state for the user in each account. - */ - private final NestedMap historyStorages; - - /** - * Chat storages for tags for users in accounts. - */ - private final NestedNestedMaps chatStorages; - - private final BaseAccountNotificationProvider availableArchiveRequestProvider; - - private final static MessageArchiveManager instance; - - static { - instance = new MessageArchiveManager(Application.getInstance()); - Application.getInstance().addManager(instance); - } - - public static MessageArchiveManager getInstance() { - return instance; - } - - private MessageArchiveManager(Application application) { - saves = new HashMap(); - defaults = new HashMap(); - items = new HashMap>>(); - sessionSaves = new NestedMap(); - modificationStorages = new HashMap(); - connected = new HashMap(); - historyStorages = new NestedMap(); - modificationRequests = new NestedMap(); - chatStorages = new NestedNestedMaps(); - availableArchiveRequestProvider = new BaseAccountNotificationProvider( - R.drawable.ic_stat_request); - } - - @Override - public void onLoad() { - Application.getInstance().runOnUiThread(new Runnable() { - @Override - public void run() { - onLoaded(); - } - }); - } - - private void onLoaded() { - NotificationManager.getInstance().registerNotificationProvider( - availableArchiveRequestProvider); - } - - @Override - public void onAccountAdded(AccountItem accountItem) { - Map> maps = new HashMap>(); - for (MatchMode matchMode : MatchMode.values()) - maps.put(matchMode, new HashMap()); - items.put(accountItem.getAccount(), maps); - } - - @Override - public void onAccountRemoved(AccountItem accountItem) { - saves.remove(accountItem.getAccount()); - defaults.remove(accountItem.getAccount()); - items.remove(accountItem.getAccount()); - sessionSaves.clear(accountItem.getAccount()); - modificationStorages.remove(accountItem.getAccount()); - connected.remove(accountItem.getAccount()); - historyStorages.clear(accountItem.getAccount()); - chatStorages.clear(accountItem.getAccount()); - } - - @Override - public void onDisconnect(ConnectionItem connection) { - if (connection instanceof AccountItem) - modificationRequests.clear(((AccountItem) connection).getAccount()); - } - - @Override - public void onTimeReceived(final ConnectionItem connection) { - if (!(connection instanceof AccountItem)) { - onModifiedAvailable(connection); - return; - } - String account = ((AccountItem) connection).getAccount(); - ModificationStorage modificationStorage = modificationStorages - .get(account); - if (modificationStorage == null) { - modificationStorage = new ModificationStorage(); - modificationStorages.put(account, modificationStorage); - } - modificationStorage.onConnected(); - removeNotReceived(account); - ArchiveMode archiveMode = AccountManager.getInstance().getArchiveMode( - account); - if (ServerInfoManager.getInstance().isProtocolSupported(account, - FEATURE_ARCH) - || ServerInfoManager.getInstance().isProtocolSupported(account, - FEATURE_PREF) - || ServerInfoManager.getInstance().isProtocolSupported(account, - FEATURE_MANAGE)) { - if (archiveMode == ArchiveMode.available) { - availableArchiveRequestProvider.add( - new AvailableArchiveRequest(account), null); - } - } - if (archiveMode != ArchiveMode.server) { - onModifiedAvailable(account); - return; - } - if (ServerInfoManager.getInstance().isProtocolSupported(account, - FEATURE_PREF)) { - requestPreferences(account); - return; - } - onPreferenceAvailable(account); - } - - /** - * Removes chat storages that wasn't fully received. - * - * @param account - */ - private void removeNotReceived(String account) { - NestedNestedMaps remove = new NestedNestedMaps(); - for (Entry> users : chatStorages - .getNested(account).entrySet()) - for (Entry storages : users.getValue() - .entrySet()) - if (!storages.getValue().isReceived()) - remove.put(account, users.getKey(), storages.getKey(), - storages.getValue()); - for (Entry> users : remove.getNested( - account).entrySet()) - for (Entry storages : users.getValue() - .entrySet()) - chatStorages.remove(account, users.getKey(), storages.getKey()); - } - - protected void onPreferenceAvailable(String account) { - if (ServerInfoManager.getInstance().isProtocolSupported(account, - FEATURE_MANAGE)) { - ModificationStorage modificationStorage = modificationStorages - .get(account); - if (modificationStorage.request(TimeManager.getInstance() - .getServerTime(account))) { - requestModified(account, ""); - return; - } - } - onModifiedAvailable(account); - } - - private void onModifiedAvailable(String account) { - modificationStorages.get(account).onFinished(); - removeNotReceived(account); - connected - .put(account, TimeManager.getInstance().getServerTime(account)); - onModifiedAvailable(AccountManager.getInstance().getAccount(account)); - if (AccountManager.getInstance().getArchiveMode(account) == ArchiveMode.server) - for (Entry entity : historyStorages - .getNested(account).entrySet()) - if (entity.getValue().onResume()) - requestSequence(account, entity.getKey(), entity.getValue()); - } - - private void onModifiedAvailable(ConnectionItem connection) { - for (OnArchiveModificationsReceivedListener listener : Application - .getInstance().getManagers( - OnArchiveModificationsReceivedListener.class)) - listener.onArchiveModificationsReceived(connection); - } - - @Override - public void onPacket(ConnectionItem connection, final String bareAddress, - Packet packet) { - if (!(connection instanceof AccountItem)) - return; - String account = ((AccountItem) connection).getAccount(); - if (AccountManager.getInstance().getArchiveMode(account) != ArchiveMode.server) - return; - if (bareAddress != null && !Jid.getServer(account).equals(bareAddress)) - return; - if (!(packet instanceof IQ)) - return; - IQ iq = (IQ) packet; - if (iq.getType() == Type.SET && packet instanceof Pref - && ((Pref) packet).isValid()) - onPreferenceReceived(account, (Pref) packet); - else if (iq.getType() == Type.SET && packet instanceof ItemRemove - && ((ItemRemove) packet).isValid()) - onItemRemoveReceived(account, (ItemRemove) packet); - else if (iq.getType() == Type.SET && packet instanceof SessionRemove - && ((SessionRemove) packet).isValid()) - onSessionRemoveReceived(account, (SessionRemove) packet); - else if (iq.getType() == Type.RESULT && packet instanceof List - && ((List) packet).isValid()) - onListReceived(account, (List) packet); - else if (iq.getType() == Type.RESULT && packet instanceof Chat - && ((Chat) packet).isValid()) - onChatReceived(account, (Chat) packet); - } - - private void onPreferencesResponce(String account, Pref pref) { - defaults.remove(account); - for (Map map : items.get(account).values()) - map.clear(); - sessionSaves.clear(account); - checkForDefaults(account, pref.getDefault()); - Boolean autoSave = pref.getAutoSave(); - if (autoSave != null) { - if (autoSave) { - // TODO: check whether record can be disabled. - } else if (AccountManager.getInstance().getArchiveMode(account) == ArchiveMode.server) { - Auto auto = new Auto(); - auto.setSave(true); - auto.setType(Type.SET); - try { - ConnectionManager.getInstance().sendPacket(account, auto); - } catch (NetworkException e) { - } - // TODO: track results. - saves.put(account, true); - } - } - onPreferenceReceived(account, pref); - } - - private void onPreferenceReceived(String account, Pref pref) { - Default defaultItem = pref.getDefault(); - if (defaultItem != null) - defaults.put(account, new ArchivePreference(defaultItem.getOtr(), - defaultItem.getSave())); - for (Item item : pref.getItems()) { - MatchMode matchMode; - String value = Jid.getStringPrep(item.getJid()); - if (item.getExactmatch() != null && item.getExactmatch()) { - matchMode = MatchMode.exect; - } else { - String resource = Jid.getResource(item.getJid()); - if (resource != null && !"".equals(resource)) - matchMode = MatchMode.exect; - else { - String name = Jid.getName(item.getJid()); - if (name != null && !"".equals(name)) { - matchMode = MatchMode.bare; - value = Jid.getBareAddress(value); - } else { - matchMode = MatchMode.domain; - value = Jid.getServer(value); - } - } - items.get(account) - .get(matchMode) - .put(value, - new ArchivePreference(item.getOtr(), item - .getSave())); - } - } - for (Session session : pref.getSessions()) - sessionSaves.put(account, session.getThread(), session.getSave()); - } - - private void checkForDefaults(String account, Default received) { - if (received == null || !received.isUnset() - || received.getSave() != SaveMode.fls) - return; - Default defaultItem = new Default(); - defaultItem.setExpire(received.getExpire()); - defaultItem.setOtr(received.getOtr()); - defaultItem.setSave(SaveMode.body); - Pref pref = new Pref(); - pref.setDefault(defaultItem); - pref.setType(Type.SET); - try { - ConnectionManager.getInstance().sendPacket(account, pref); - } catch (NetworkException e) { - } - } - - private void onItemRemoveReceived(String account, ItemRemove itemRemove) { - for (Item item : itemRemove.getItems()) - for (Map map : items.get(account) - .values()) - map.remove(item.getJid()); - } - - private void onSessionRemoveReceived(String account, - SessionRemove sessionRemove) { - for (Session session : sessionRemove.getSessions()) - sessionSaves.remove(account, session.getThread()); - } - - private void onModifiedReceived(String account, Modified modified) { - ModificationStorage modificationStorage = modificationStorages - .get(account); - onHeadersReceived(account, null, modificationStorage, - modified.getChats(), modified.getRsm()); - } - - private void onListReceived(String account, List list) { - String bareAddress = null; - HistoryStorage historyStorage = null; - for (Entry entry : historyStorages.getNested( - account).entrySet()) - if (entry.getValue().hasPacketId(list.getPacketID())) { - bareAddress = entry.getKey(); - historyStorage = entry.getValue(); - } - if (bareAddress == null) - return; - historyStorage.setPacketId(null); - onHeadersReceived(account, bareAddress, historyStorage, - list.getChats(), list.getRsm()); - } - - private void onHeadersReceived(String account, String bareAddress, - HeaderSequence sequence, - Collection headers, Set rsm) { - sequence.addHeaders(headers); - if (rsm == null || rsm.isBackwardFinished(headers.size())) - sequence.onHeadersReceived(); - else - sequence.setNext(rsm.getFirst()); - requestSequence(account, bareAddress, sequence); - } - - private void onChatReceived(String account, Chat chat) { - Boolean modification = modificationRequests.remove(account, - chat.getPacketID()); - if (modification == null) - return; - ChatStorage chatStorage = chatStorages.get(account, chat.getWith(), - chat.getStartString()); - if (chatStorage == null) { - LogManager.w(this, "Unexpected chat " + chat.getStartString() - + " recevied by " + account + " from " + chat.getWith()); - chatStorage = new ChatStorage(chat.getStart()); - chatStorages.put(account, chat.getWith(), chat.getStartString(), - chatStorage); - } - String bareAddress = Jid.getBareAddress(chat.getWith()); - HeaderSequence sequence; - if (modification) - sequence = modificationStorages.get(account); - else - sequence = historyStorages.get(account, bareAddress); - if (sequence == null) - return; - AbstractChat abstractChat = MessageManager.getInstance() - .getOrCreateChat(account, bareAddress); - for (AbstractMessage abstractMessage : chat.getMessages()) - chatStorage.addItem(abstractChat, chat, abstractMessage, - TimeManager.getInstance().getServerTimeOffset(account)); - if (chat.getRsm() == null - || chat.getRsm().isForwardFinished(chat.getMessages().size())) { - chatStorage.onItemsReceived(chat.getVersion()); - sequence.pollHeader(); - if (sequence instanceof HistoryStorage) - if (apply(account, bareAddress, chat.getStartString(), - chatStorage, (HistoryStorage) sequence)) - return; - requestSequence(account, bareAddress, sequence); - } else { - requestChat(account, chat, chat.getRsm().getLast(), modification); - } - } - - /** - * Apply received messages. - * - * @param account - * @param bareAddress - * @param tag - * @param chatStorage - * @param historyStorage - * @return Whether enough messages have been received. - */ - private boolean apply(String account, String bareAddress, String tag, - ChatStorage chatStorage, HistoryStorage historyStorage) { - AbstractChat abstractChat = MessageManager.getInstance().getChat( - account, bareAddress); - int newCount = abstractChat.onMessageDownloaded(tag, - chatStorage.getItems(), false); - int incomingCount = 0; - for (MessageItem messageItem : chatStorage.getItems()) - if (messageItem.isIncoming()) - incomingCount += 1; - chatStorage.onApplied(); - if (historyStorage.enoughMessages(newCount, incomingCount)) { - historyStorage.onSuccess(); - return true; - } - return false; - } - - /** - * Apply received messages. - * - * @param account - * @param modificationStorage - */ - private void apply(String account, ModificationStorage modificationStorage) { - for (Entry> users : chatStorages - .getNested(account).entrySet()) { - String bareAddress = Jid.getBareAddress(users.getKey()); - AbstractChat abstractChat = MessageManager.getInstance().getChat( - account, bareAddress); - for (Entry storages : users.getValue() - .entrySet()) { - ChatStorage chatStorage = storages.getValue(); - if (chatStorage.isApplied()) - continue; - abstractChat.onMessageDownloaded(storages.getKey(), - chatStorage.getItems(), true); - chatStorage.onApplied(); - } - } - modificationStorage.onSuccess(); - } - - private void requestPreferences(String account) { - Pref pref = new Pref(); - pref.setType(Type.GET); - try { - ConnectionManager.getInstance().sendRequest(account, pref, - new OnResponseListener() { - - @Override - public void onReceived(String account, String packetId, - IQ iq) { - if (iq instanceof Pref && ((Pref) iq).isValid()) - onPreferencesResponce(account, (Pref) iq); - onPreferenceAvailable(account); - } - - @Override - public void onError(String account, String packetId, - IQ iq) { - onPreferenceAvailable(account); - } - - @Override - public void onTimeout(String account, String packetId) { - onError(account, packetId, null); - } - - @Override - public void onDisconnect(String account, String packetId) { - } - - }); - } catch (NetworkException e) { - } - } - - private void requestSequence(String account, String bareAddress, - HeaderSequence sequence) { - while (true) { - CollectionHeader header = sequence.peekHeader(); - if (header == null) { - if (sequence.isHeadersReceived()) { - if (sequence instanceof ModificationStorage) { - apply(account, (ModificationStorage) sequence); - onModifiedAvailable(account); - } - } else { - if (sequence instanceof ModificationStorage) { - requestModified(account, sequence.getNext()); - } else { - ((HistoryStorage) sequence).setPacketId(requestList( - account, bareAddress, sequence.getNext())); - } - } - } else { - ChatStorage chatStorage = chatStorages.get(account, - header.getWith(), header.getStartString()); - if (chatStorage == null - || (chatStorage.isReceived() && !chatStorage - .hasVersion(header.getVersion()))) { - chatStorage = new ChatStorage(header.getStart()); - chatStorages.put(account, header.getWith(), - header.getStartString(), chatStorage); - } else if (chatStorage.isReceived()) { - if (sequence instanceof HistoryStorage - && !chatStorage.isApplied()) - if (apply(account, header.getWith(), - header.getStartString(), chatStorage, - (HistoryStorage) sequence)) - break; - sequence.pollHeader(); - continue; - } - requestChat(account, header, null, - sequence instanceof ModificationStorage); - } - break; - } - } - - private void requestModified(String account, String before) { - Modified packet = new Modified(); - packet.setType(Type.GET); - Set rsm = new Set(); - rsm.setMax(RSM_MAX); - rsm.setBefore(before); - packet.setRsm(rsm); - packet.setStart(modificationStorages.get(account).getLastRequest()); - try { - ConnectionManager.getInstance().sendRequest(account, packet, - new OnResponseListener() { - - @Override - public void onReceived(String account, String packetId, - IQ iq) { - if (iq instanceof Modified - && ((Modified) iq).isValid()) - onModifiedReceived(account, (Modified) iq); - else - onError(account, packetId, iq); - } - - @Override - public void onError(String account, String packetId, - IQ iq) { - onModifiedAvailable(account); - } - - @Override - public void onTimeout(String account, String packetId) { - onError(account, packetId, null); - } - - @Override - public void onDisconnect(String account, String packetId) { - } - - }); - } catch (NetworkException e) { - } - } - - /** - * At east newCount new messages and incomingCount - * incoming messages should be loaded from the server side history. - * - * @param account - * @param bareAddress - * @param newCount - * @param incomingCount - */ - public void requestHistory(String account, String bareAddress, - int newCount, int incomingCount) { - if (AccountManager.getInstance().getArchiveMode(account) != ArchiveMode.server - || (newCount <= 0 && incomingCount <= 0)) - return; - HistoryStorage historyStorage = historyStorages.get(account, - bareAddress); - if (historyStorage == null) { - historyStorage = new HistoryStorage(); - historyStorages.put(account, bareAddress, historyStorage); - } - ModificationStorage modificationStorage = modificationStorages - .get(account); - if (historyStorage.isInProgress() - || (modificationStorage != null && modificationStorage - .isInProgress())) { - historyStorage.setRequestedCountAtLeast(newCount, incomingCount); - return; - } - historyStorage.onRequest(newCount, incomingCount); - requestSequence(account, bareAddress, historyStorage); - } - - private String requestList(String account, String bareAddress, String before) { - List packet = new List(); - packet.setType(Type.GET); - Set rsm = new Set(); - rsm.setMax(RSM_MAX); - rsm.setBefore(before); - packet.setRsm(rsm); - packet.setWith(bareAddress); - packet.setEnd(connected.get(account)); - String packetId = packet.getPacketID(); - try { - ConnectionManager.getInstance().sendPacket(account, packet); - } catch (NetworkException e) { - } - return packetId; - } - - private void requestChat(String account, CollectionHeader header, - String after, boolean modification) { - Retrieve packet = new Retrieve(); - packet.setType(Type.GET); - Set rsm = new Set(); - rsm.setMax(RSM_MAX); - rsm.setAfter(after); - packet.setRsm(rsm); - packet.setWith(header.getWith()); - packet.setStartString(header.getStartString()); - modificationRequests.put(account, packet.getPacketID(), modification); - try { - if (!modification) { - ConnectionManager.getInstance().sendPacket(account, packet); - return; - } - ConnectionManager.getInstance().sendRequest(account, packet, - new OnResponseListener() { - - @Override - public void onReceived(String account, String packetId, - IQ iq) { - if (iq instanceof Chat && ((Chat) iq).isValid()) - onChatReceived(account, (Chat) iq); - else - onError(account, packetId, iq); - } - - @Override - public void onError(String account, String packetId, - IQ iq) { - onModifiedAvailable(account); - } - - @Override - public void onTimeout(String account, String packetId) { - onError(account, packetId, null); - } - - @Override - public void onDisconnect(String account, String packetId) { - } - - }); - } catch (NetworkException e) { - } - } - - private void sendItemUpdate(String account, String user, SaveMode saveMode, - OtrMode otrMode) throws NetworkException { - Item extension = new Item(); - extension.setJid(user); - extension.setOtr(otrMode); - extension.setSave(saveMode); - Pref packet = new Pref(); - packet.addItem(extension); - packet.setType(Type.SET); - ConnectionManager.getInstance().sendPacket(account, packet); - } - - private void sendItemRemove(String account, String user) - throws NetworkException { - Item extension = new Item(); - extension.setJid(user); - ItemRemove packet = new ItemRemove(); - packet.addItem(extension); - packet.setType(Type.SET); - ConnectionManager.getInstance().sendPacket(account, packet); - } - - public void setOtrMode(String account, String user, OtrMode otrMode) - throws NetworkException { - ArchivePreference itemArchivePreference = getItemArchivePreference( - account, user); - if (itemArchivePreference != null - && itemArchivePreference.getOtrMode() == otrMode) - return; - ArchivePreference defaultArchivePreference = defaults.get(account); - SaveMode userSaveMode = getUserSaveMode(account, user); - if (userSaveMode == null) { - if (otrMode == OtrMode.require) - userSaveMode = SaveMode.fls; - else - userSaveMode = SaveMode.body; - } - if (itemArchivePreference == null) { - if (defaultArchivePreference != null - && defaultArchivePreference.getOtrMode() == otrMode) - return; - else - sendItemUpdate(account, user, userSaveMode, otrMode); - } else { - if (defaultArchivePreference != null - && defaultArchivePreference.getOtrMode() == otrMode) - sendItemRemove(account, user); - else - sendItemUpdate(account, user, userSaveMode, otrMode); - } - } - - /** - * @param account - * @param user - * @return Selected OTR mode or null if there is no either user - * settings either default settings. - */ - public OtrMode getOtrMode(String account, String user) { - ArchivePreference archivePreference = getItemArchivePreference(account, - user); - if (archivePreference == null) - archivePreference = defaults.get(account); - if (archivePreference == null) - return null; - return archivePreference.getOtrMode(); - } - - /** - * @param account - * @param user - * @param session - * @return null if there is no session, user and default - * settings. - */ - public SaveMode getSaveMode(String account, String user, String session) { - SaveMode sessionSaveMode = sessionSaves.get(account, session); - if (sessionSaveMode != null) - return sessionSaveMode; - return getUserSaveMode(account, user); - } - - private ArchivePreference getItemArchivePreference(String account, - String user) { - Map> map = items.get(account); - if (map == null) - return null; - ArchivePreference result = map.get(MatchMode.exect).get(user); - if (result != null) - return result; - result = map.get(MatchMode.bare).get(Jid.getBareAddress(user)); - if (result != null) - return result; - return map.get(MatchMode.domain).get(Jid.getServer(user)); - } - - private void sendSessionUpdate(String account, String session, - SaveMode saveMode) throws NetworkException { - Session extension = new Session(); - extension.setThread(session); - extension.setTimeout(SESSION_TIMEOUT); - extension.setSave(saveMode); - Pref packet = new Pref(); - packet.addSession(extension); - packet.setType(Type.SET); - ConnectionManager.getInstance().sendPacket(account, packet); - sessionSaves.put(account, session, saveMode); - } - - private void sendSessionRemove(String account, String session) - throws NetworkException { - Session extension = new Session(); - extension.setThread(session); - SessionRemove packet = new SessionRemove(); - packet.addSession(extension); - packet.setType(Type.SET); - ConnectionManager.getInstance().sendPacket(account, packet); - sessionSaves.remove(account, session); - } - - private SaveMode getUserSaveMode(String account, String user) { - ArchivePreference archivePreference = getItemArchivePreference(account, - user); - if (archivePreference != null) - return archivePreference.getSaveMode(); - Boolean save = saves.get(account); - if (save != null) - return save ? SaveMode.body : SaveMode.fls; - archivePreference = defaults.get(account); - if (archivePreference != null) - return archivePreference.getSaveMode(); - return null; - } - - public void setSaveMode(String account, String user, String session, - SaveMode saveMode) throws NetworkException { - if (AccountManager.getInstance().getArchiveMode(account) != ArchiveMode.server) - return; - SaveMode sessionSaveMode = sessionSaves.get(account, session); - if (sessionSaveMode == saveMode) - return; - SaveMode userSaveMode = getUserSaveMode(account, user); - if (sessionSaveMode == null) { - if (userSaveMode == saveMode) - return; - else - sendSessionUpdate(account, session, saveMode); - } else { - if (userSaveMode == saveMode) - sendSessionRemove(account, session); - else - sendSessionUpdate(account, session, saveMode); - } - } - - public boolean isModificationsSucceed(String account) { - ModificationStorage modificationStorage = modificationStorages - .get(account); - if (modificationStorage == null) - return false; - return modificationStorage.isSucceed(); - } - - @Override - public void onAccountArchiveModeChanged(AccountItem accountItem) { - availableArchiveRequestProvider.remove(accountItem.getAccount()); - } + OnTimeReceivedListener, OnAccountAddedListener, + OnAccountRemovedListener, OnLoadListener, + OnAccountArchiveModeChangedListener, OnDisconnectListener { + + private static final Integer SESSION_TIMEOUT = 7 * 24 * 60 * 60; + + private static final int RSM_MAX = 20; + + private static final String FEATURE_ARCH = "urn:xmpp:archive"; + private static final String FEATURE_PREF = "urn:xmpp:archive:pref"; + private static final String FEATURE_MANAGE = "urn:xmpp:archive:manage"; + private final static MessageArchiveManager instance; + + static { + instance = new MessageArchiveManager(Application.getInstance()); + Application.getInstance().addManager(instance); + } + + /** + * Custom auto setting per account. + */ + private final Map saves; + /** + * Default settings for each account. + */ + private final Map defaults; + /** + * Per user settings for each account. + */ + private final Map>> items; + /** + * Settings for session in each account. + */ + private final NestedMap sessionSaves; + /** + * Contains whether chat modification has been requested for the given + * packet id in the accounts. + */ + private final NestedMap modificationRequests; + /** + * Store information about modifications received from the server. + */ + private final Map modificationStorages; + /** + * Server side timestamp when connection has been established. + */ + private final Map connected; + /** + * Store current history request state for the user in each account. + */ + private final NestedMap historyStorages; + /** + * Chat storages for tags for users in accounts. + */ + private final NestedNestedMaps chatStorages; + private final BaseAccountNotificationProvider availableArchiveRequestProvider; + + private MessageArchiveManager(Application application) { + saves = new HashMap(); + defaults = new HashMap(); + items = new HashMap>>(); + sessionSaves = new NestedMap(); + modificationStorages = new HashMap(); + connected = new HashMap(); + historyStorages = new NestedMap(); + modificationRequests = new NestedMap(); + chatStorages = new NestedNestedMaps(); + availableArchiveRequestProvider = new BaseAccountNotificationProvider( + R.drawable.ic_stat_help); + } + + public static MessageArchiveManager getInstance() { + return instance; + } + + @Override + public void onLoad() { + Application.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + onLoaded(); + } + }); + } + + private void onLoaded() { + NotificationManager.getInstance().registerNotificationProvider( + availableArchiveRequestProvider); + } + + @Override + public void onAccountAdded(AccountItem accountItem) { + Map> maps = new HashMap>(); + for (MatchMode matchMode : MatchMode.values()) + maps.put(matchMode, new HashMap()); + items.put(accountItem.getAccount(), maps); + } + + @Override + public void onAccountRemoved(AccountItem accountItem) { + saves.remove(accountItem.getAccount()); + defaults.remove(accountItem.getAccount()); + items.remove(accountItem.getAccount()); + sessionSaves.clear(accountItem.getAccount()); + modificationStorages.remove(accountItem.getAccount()); + connected.remove(accountItem.getAccount()); + historyStorages.clear(accountItem.getAccount()); + chatStorages.clear(accountItem.getAccount()); + } + + @Override + public void onDisconnect(ConnectionItem connection) { + if (connection instanceof AccountItem) + modificationRequests.clear(((AccountItem) connection).getAccount()); + } + + @Override + public void onTimeReceived(final ConnectionItem connection) { + if (!(connection instanceof AccountItem)) { + onModifiedAvailable(connection); + return; + } + String account = ((AccountItem) connection).getAccount(); + ModificationStorage modificationStorage = modificationStorages + .get(account); + if (modificationStorage == null) { + modificationStorage = new ModificationStorage(); + modificationStorages.put(account, modificationStorage); + } + modificationStorage.onConnected(); + removeNotReceived(account); + ArchiveMode archiveMode = AccountManager.getInstance().getArchiveMode( + account); + if (ServerInfoManager.getInstance().isProtocolSupported(account, + FEATURE_ARCH) + || ServerInfoManager.getInstance().isProtocolSupported(account, + FEATURE_PREF) + || ServerInfoManager.getInstance().isProtocolSupported(account, + FEATURE_MANAGE)) { + if (archiveMode == ArchiveMode.available) { + availableArchiveRequestProvider.add( + new AvailableArchiveRequest(account), null); + } + } + if (archiveMode != ArchiveMode.server) { + onModifiedAvailable(account); + return; + } + if (ServerInfoManager.getInstance().isProtocolSupported(account, + FEATURE_PREF)) { + requestPreferences(account); + return; + } + onPreferenceAvailable(account); + } + + /** + * Removes chat storages that wasn't fully received. + * + * @param account + */ + private void removeNotReceived(String account) { + NestedNestedMaps remove = new NestedNestedMaps(); + for (Entry> users : chatStorages + .getNested(account).entrySet()) + for (Entry storages : users.getValue() + .entrySet()) + if (!storages.getValue().isReceived()) + remove.put(account, users.getKey(), storages.getKey(), + storages.getValue()); + for (Entry> users : remove.getNested( + account).entrySet()) + for (Entry storages : users.getValue() + .entrySet()) + chatStorages.remove(account, users.getKey(), storages.getKey()); + } + + protected void onPreferenceAvailable(String account) { + if (ServerInfoManager.getInstance().isProtocolSupported(account, + FEATURE_MANAGE)) { + ModificationStorage modificationStorage = modificationStorages + .get(account); + if (modificationStorage.request(TimeManager.getInstance() + .getServerTime(account))) { + requestModified(account, ""); + return; + } + } + onModifiedAvailable(account); + } + + private void onModifiedAvailable(String account) { + modificationStorages.get(account).onFinished(); + removeNotReceived(account); + connected + .put(account, TimeManager.getInstance().getServerTime(account)); + onModifiedAvailable(AccountManager.getInstance().getAccount(account)); + if (AccountManager.getInstance().getArchiveMode(account) == ArchiveMode.server) + for (Entry entity : historyStorages + .getNested(account).entrySet()) + if (entity.getValue().onResume()) + requestSequence(account, entity.getKey(), entity.getValue()); + } + + private void onModifiedAvailable(ConnectionItem connection) { + for (OnArchiveModificationsReceivedListener listener : Application + .getInstance().getManagers( + OnArchiveModificationsReceivedListener.class)) + listener.onArchiveModificationsReceived(connection); + } + + @Override + public void onPacket(ConnectionItem connection, final String bareAddress, + Packet packet) { + if (!(connection instanceof AccountItem)) + return; + String account = ((AccountItem) connection).getAccount(); + if (AccountManager.getInstance().getArchiveMode(account) != ArchiveMode.server) + return; + if (bareAddress != null && !Jid.getServer(account).equals(bareAddress)) + return; + if (!(packet instanceof IQ)) + return; + IQ iq = (IQ) packet; + if (iq.getType() == Type.SET && packet instanceof Pref + && ((Pref) packet).isValid()) + onPreferenceReceived(account, (Pref) packet); + else if (iq.getType() == Type.SET && packet instanceof ItemRemove + && ((ItemRemove) packet).isValid()) + onItemRemoveReceived(account, (ItemRemove) packet); + else if (iq.getType() == Type.SET && packet instanceof SessionRemove + && ((SessionRemove) packet).isValid()) + onSessionRemoveReceived(account, (SessionRemove) packet); + else if (iq.getType() == Type.RESULT && packet instanceof List + && ((List) packet).isValid()) + onListReceived(account, (List) packet); + else if (iq.getType() == Type.RESULT && packet instanceof Chat + && ((Chat) packet).isValid()) + onChatReceived(account, (Chat) packet); + } + + private void onPreferencesResponce(String account, Pref pref) { + defaults.remove(account); + for (Map map : items.get(account).values()) + map.clear(); + sessionSaves.clear(account); + checkForDefaults(account, pref.getDefault()); + Boolean autoSave = pref.getAutoSave(); + if (autoSave != null) { + if (autoSave) { + // TODO: check whether record can be disabled. + } else if (AccountManager.getInstance().getArchiveMode(account) == ArchiveMode.server) { + Auto auto = new Auto(); + auto.setSave(true); + auto.setType(Type.SET); + try { + ConnectionManager.getInstance().sendPacket(account, auto); + } catch (NetworkException e) { + } + // TODO: track results. + saves.put(account, true); + } + } + onPreferenceReceived(account, pref); + } + + private void onPreferenceReceived(String account, Pref pref) { + Default defaultItem = pref.getDefault(); + if (defaultItem != null) + defaults.put(account, new ArchivePreference(defaultItem.getOtr(), + defaultItem.getSave())); + for (Item item : pref.getItems()) { + MatchMode matchMode; + String value = Jid.getStringPrep(item.getJid()); + if (item.getExactmatch() != null && item.getExactmatch()) { + matchMode = MatchMode.exect; + } else { + String resource = Jid.getResource(item.getJid()); + if (resource != null && !"".equals(resource)) + matchMode = MatchMode.exect; + else { + String name = Jid.getName(item.getJid()); + if (name != null && !"".equals(name)) { + matchMode = MatchMode.bare; + value = Jid.getBareAddress(value); + } else { + matchMode = MatchMode.domain; + value = Jid.getServer(value); + } + } + items.get(account) + .get(matchMode) + .put(value, + new ArchivePreference(item.getOtr(), item + .getSave())); + } + } + for (Session session : pref.getSessions()) + sessionSaves.put(account, session.getThread(), session.getSave()); + } + + private void checkForDefaults(String account, Default received) { + if (received == null || !received.isUnset() + || received.getSave() != SaveMode.fls) + return; + Default defaultItem = new Default(); + defaultItem.setExpire(received.getExpire()); + defaultItem.setOtr(received.getOtr()); + defaultItem.setSave(SaveMode.body); + Pref pref = new Pref(); + pref.setDefault(defaultItem); + pref.setType(Type.SET); + try { + ConnectionManager.getInstance().sendPacket(account, pref); + } catch (NetworkException e) { + } + } + + private void onItemRemoveReceived(String account, ItemRemove itemRemove) { + for (Item item : itemRemove.getItems()) + for (Map map : items.get(account) + .values()) + map.remove(item.getJid()); + } + + private void onSessionRemoveReceived(String account, + SessionRemove sessionRemove) { + for (Session session : sessionRemove.getSessions()) + sessionSaves.remove(account, session.getThread()); + } + + private void onModifiedReceived(String account, Modified modified) { + ModificationStorage modificationStorage = modificationStorages + .get(account); + onHeadersReceived(account, null, modificationStorage, + modified.getChats(), modified.getRsm()); + } + + private void onListReceived(String account, List list) { + String bareAddress = null; + HistoryStorage historyStorage = null; + for (Entry entry : historyStorages.getNested( + account).entrySet()) + if (entry.getValue().hasPacketId(list.getPacketID())) { + bareAddress = entry.getKey(); + historyStorage = entry.getValue(); + } + if (bareAddress == null) + return; + historyStorage.setPacketId(null); + onHeadersReceived(account, bareAddress, historyStorage, + list.getChats(), list.getRsm()); + } + + private void onHeadersReceived(String account, String bareAddress, + HeaderSequence sequence, + Collection headers, Set rsm) { + sequence.addHeaders(headers); + if (rsm == null || rsm.isBackwardFinished(headers.size())) + sequence.onHeadersReceived(); + else + sequence.setNext(rsm.getFirst()); + requestSequence(account, bareAddress, sequence); + } + + private void onChatReceived(String account, Chat chat) { + Boolean modification = modificationRequests.remove(account, + chat.getPacketID()); + if (modification == null) + return; + ChatStorage chatStorage = chatStorages.get(account, chat.getWith(), + chat.getStartString()); + if (chatStorage == null) { + LogManager.w(this, "Unexpected chat " + chat.getStartString() + + " recevied by " + account + " from " + chat.getWith()); + chatStorage = new ChatStorage(chat.getStart()); + chatStorages.put(account, chat.getWith(), chat.getStartString(), + chatStorage); + } + String bareAddress = Jid.getBareAddress(chat.getWith()); + HeaderSequence sequence; + if (modification) + sequence = modificationStorages.get(account); + else + sequence = historyStorages.get(account, bareAddress); + if (sequence == null) + return; + AbstractChat abstractChat = MessageManager.getInstance() + .getOrCreateChat(account, bareAddress); + for (AbstractMessage abstractMessage : chat.getMessages()) + chatStorage.addItem(abstractChat, chat, abstractMessage, + TimeManager.getInstance().getServerTimeOffset(account)); + if (chat.getRsm() == null + || chat.getRsm().isForwardFinished(chat.getMessages().size())) { + chatStorage.onItemsReceived(chat.getVersion()); + sequence.pollHeader(); + if (sequence instanceof HistoryStorage) + if (apply(account, bareAddress, chat.getStartString(), + chatStorage, (HistoryStorage) sequence)) + return; + requestSequence(account, bareAddress, sequence); + } else { + requestChat(account, chat, chat.getRsm().getLast(), modification); + } + } + + /** + * Apply received messages. + * + * @param account + * @param bareAddress + * @param tag + * @param chatStorage + * @param historyStorage + * @return Whether enough messages have been received. + */ + private boolean apply(String account, String bareAddress, String tag, + ChatStorage chatStorage, HistoryStorage historyStorage) { + AbstractChat abstractChat = MessageManager.getInstance().getChat( + account, bareAddress); + int newCount = abstractChat.onMessageDownloaded(tag, + chatStorage.getItems(), false); + int incomingCount = 0; + for (MessageItem messageItem : chatStorage.getItems()) + if (messageItem.isIncoming()) + incomingCount += 1; + chatStorage.onApplied(); + if (historyStorage.enoughMessages(newCount, incomingCount)) { + historyStorage.onSuccess(); + return true; + } + return false; + } + + /** + * Apply received messages. + * + * @param account + * @param modificationStorage + */ + private void apply(String account, ModificationStorage modificationStorage) { + for (Entry> users : chatStorages + .getNested(account).entrySet()) { + String bareAddress = Jid.getBareAddress(users.getKey()); + AbstractChat abstractChat = MessageManager.getInstance().getChat( + account, bareAddress); + for (Entry storages : users.getValue() + .entrySet()) { + ChatStorage chatStorage = storages.getValue(); + if (chatStorage.isApplied()) + continue; + abstractChat.onMessageDownloaded(storages.getKey(), + chatStorage.getItems(), true); + chatStorage.onApplied(); + } + } + modificationStorage.onSuccess(); + } + + private void requestPreferences(String account) { + Pref pref = new Pref(); + pref.setType(Type.GET); + try { + ConnectionManager.getInstance().sendRequest(account, pref, + new OnResponseListener() { + + @Override + public void onReceived(String account, String packetId, + IQ iq) { + if (iq instanceof Pref && ((Pref) iq).isValid()) + onPreferencesResponce(account, (Pref) iq); + onPreferenceAvailable(account); + } + + @Override + public void onError(String account, String packetId, + IQ iq) { + onPreferenceAvailable(account); + } + + @Override + public void onTimeout(String account, String packetId) { + onError(account, packetId, null); + } + + @Override + public void onDisconnect(String account, String packetId) { + } + + }); + } catch (NetworkException e) { + } + } + + private void requestSequence(String account, String bareAddress, + HeaderSequence sequence) { + while (true) { + CollectionHeader header = sequence.peekHeader(); + if (header == null) { + if (sequence.isHeadersReceived()) { + if (sequence instanceof ModificationStorage) { + apply(account, (ModificationStorage) sequence); + onModifiedAvailable(account); + } + } else { + if (sequence instanceof ModificationStorage) { + requestModified(account, sequence.getNext()); + } else { + ((HistoryStorage) sequence).setPacketId(requestList( + account, bareAddress, sequence.getNext())); + } + } + } else { + ChatStorage chatStorage = chatStorages.get(account, + header.getWith(), header.getStartString()); + if (chatStorage == null + || (chatStorage.isReceived() && !chatStorage + .hasVersion(header.getVersion()))) { + chatStorage = new ChatStorage(header.getStart()); + chatStorages.put(account, header.getWith(), + header.getStartString(), chatStorage); + } else if (chatStorage.isReceived()) { + if (sequence instanceof HistoryStorage + && !chatStorage.isApplied()) + if (apply(account, header.getWith(), + header.getStartString(), chatStorage, + (HistoryStorage) sequence)) + break; + sequence.pollHeader(); + continue; + } + requestChat(account, header, null, + sequence instanceof ModificationStorage); + } + break; + } + } + + private void requestModified(String account, String before) { + Modified packet = new Modified(); + packet.setType(Type.GET); + Set rsm = new Set(); + rsm.setMax(RSM_MAX); + rsm.setBefore(before); + packet.setRsm(rsm); + packet.setStart(modificationStorages.get(account).getLastRequest()); + try { + ConnectionManager.getInstance().sendRequest(account, packet, + new OnResponseListener() { + + @Override + public void onReceived(String account, String packetId, + IQ iq) { + if (iq instanceof Modified + && ((Modified) iq).isValid()) + onModifiedReceived(account, (Modified) iq); + else + onError(account, packetId, iq); + } + + @Override + public void onError(String account, String packetId, + IQ iq) { + onModifiedAvailable(account); + } + + @Override + public void onTimeout(String account, String packetId) { + onError(account, packetId, null); + } + + @Override + public void onDisconnect(String account, String packetId) { + } + + }); + } catch (NetworkException e) { + } + } + + /** + * At east newCount new messages and incomingCount + * incoming messages should be loaded from the server side history. + * + * @param account + * @param bareAddress + * @param newCount + * @param incomingCount + */ + public void requestHistory(String account, String bareAddress, int newCount, int incomingCount) { + if (AccountManager.getInstance().getArchiveMode(account) != ArchiveMode.server + || (newCount <= 0 && incomingCount <= 0)) + return; + HistoryStorage historyStorage = historyStorages.get(account, + bareAddress); + if (historyStorage == null) { + historyStorage = new HistoryStorage(); + historyStorages.put(account, bareAddress, historyStorage); + } + ModificationStorage modificationStorage = modificationStorages + .get(account); + if (historyStorage.isInProgress() + || (modificationStorage != null && modificationStorage + .isInProgress())) { + historyStorage.setRequestedCountAtLeast(newCount, incomingCount); + return; + } + historyStorage.onRequest(newCount, incomingCount); + requestSequence(account, bareAddress, historyStorage); + } + + private String requestList(String account, String bareAddress, String before) { + List packet = new List(); + packet.setType(Type.GET); + Set rsm = new Set(); + rsm.setMax(RSM_MAX); + rsm.setBefore(before); + packet.setRsm(rsm); + packet.setWith(bareAddress); + packet.setEnd(connected.get(account)); + String packetId = packet.getPacketID(); + try { + ConnectionManager.getInstance().sendPacket(account, packet); + } catch (NetworkException e) { + } + return packetId; + } + + private void requestChat(String account, CollectionHeader header, + String after, boolean modification) { + Retrieve packet = new Retrieve(); + packet.setType(Type.GET); + Set rsm = new Set(); + rsm.setMax(RSM_MAX); + rsm.setAfter(after); + packet.setRsm(rsm); + packet.setWith(header.getWith()); + packet.setStartString(header.getStartString()); + modificationRequests.put(account, packet.getPacketID(), modification); + try { + if (!modification) { + ConnectionManager.getInstance().sendPacket(account, packet); + return; + } + ConnectionManager.getInstance().sendRequest(account, packet, + new OnResponseListener() { + + @Override + public void onReceived(String account, String packetId, + IQ iq) { + if (iq instanceof Chat && ((Chat) iq).isValid()) + onChatReceived(account, (Chat) iq); + else + onError(account, packetId, iq); + } + + @Override + public void onError(String account, String packetId, + IQ iq) { + onModifiedAvailable(account); + } + + @Override + public void onTimeout(String account, String packetId) { + onError(account, packetId, null); + } + + @Override + public void onDisconnect(String account, String packetId) { + } + + }); + } catch (NetworkException e) { + } + } + + private void sendItemUpdate(String account, String user, SaveMode saveMode, + OtrMode otrMode) throws NetworkException { + Item extension = new Item(); + extension.setJid(user); + extension.setOtr(otrMode); + extension.setSave(saveMode); + Pref packet = new Pref(); + packet.addItem(extension); + packet.setType(Type.SET); + ConnectionManager.getInstance().sendPacket(account, packet); + } + + private void sendItemRemove(String account, String user) + throws NetworkException { + Item extension = new Item(); + extension.setJid(user); + ItemRemove packet = new ItemRemove(); + packet.addItem(extension); + packet.setType(Type.SET); + ConnectionManager.getInstance().sendPacket(account, packet); + } + + public void setOtrMode(String account, String user, OtrMode otrMode) + throws NetworkException { + ArchivePreference itemArchivePreference = getItemArchivePreference( + account, user); + if (itemArchivePreference != null + && itemArchivePreference.getOtrMode() == otrMode) + return; + ArchivePreference defaultArchivePreference = defaults.get(account); + SaveMode userSaveMode = getUserSaveMode(account, user); + if (userSaveMode == null) { + if (otrMode == OtrMode.require) + userSaveMode = SaveMode.fls; + else + userSaveMode = SaveMode.body; + } + if (itemArchivePreference == null) { + if (defaultArchivePreference != null + && defaultArchivePreference.getOtrMode() == otrMode) + return; + else + sendItemUpdate(account, user, userSaveMode, otrMode); + } else { + if (defaultArchivePreference != null + && defaultArchivePreference.getOtrMode() == otrMode) + sendItemRemove(account, user); + else + sendItemUpdate(account, user, userSaveMode, otrMode); + } + } + + /** + * @param account + * @param user + * @return Selected OTR mode or null if there is no either user + * settings either default settings. + */ + public OtrMode getOtrMode(String account, String user) { + ArchivePreference archivePreference = getItemArchivePreference(account, + user); + if (archivePreference == null) + archivePreference = defaults.get(account); + if (archivePreference == null) + return null; + return archivePreference.getOtrMode(); + } + + /** + * @param account + * @param user + * @param session + * @return null if there is no session, user and default + * settings. + */ + public SaveMode getSaveMode(String account, String user, String session) { + SaveMode sessionSaveMode = sessionSaves.get(account, session); + if (sessionSaveMode != null) + return sessionSaveMode; + return getUserSaveMode(account, user); + } + + private ArchivePreference getItemArchivePreference(String account, + String user) { + Map> map = items.get(account); + if (map == null) + return null; + ArchivePreference result = map.get(MatchMode.exect).get(user); + if (result != null) + return result; + result = map.get(MatchMode.bare).get(Jid.getBareAddress(user)); + if (result != null) + return result; + return map.get(MatchMode.domain).get(Jid.getServer(user)); + } + + private void sendSessionUpdate(String account, String session, + SaveMode saveMode) throws NetworkException { + Session extension = new Session(); + extension.setThread(session); + extension.setTimeout(SESSION_TIMEOUT); + extension.setSave(saveMode); + Pref packet = new Pref(); + packet.addSession(extension); + packet.setType(Type.SET); + ConnectionManager.getInstance().sendPacket(account, packet); + sessionSaves.put(account, session, saveMode); + } + + private void sendSessionRemove(String account, String session) + throws NetworkException { + Session extension = new Session(); + extension.setThread(session); + SessionRemove packet = new SessionRemove(); + packet.addSession(extension); + packet.setType(Type.SET); + ConnectionManager.getInstance().sendPacket(account, packet); + sessionSaves.remove(account, session); + } + + private SaveMode getUserSaveMode(String account, String user) { + ArchivePreference archivePreference = getItemArchivePreference(account, + user); + if (archivePreference != null) + return archivePreference.getSaveMode(); + Boolean save = saves.get(account); + if (save != null) + return save ? SaveMode.body : SaveMode.fls; + archivePreference = defaults.get(account); + if (archivePreference != null) + return archivePreference.getSaveMode(); + return null; + } + + public void setSaveMode(String account, String user, String session, + SaveMode saveMode) throws NetworkException { + if (AccountManager.getInstance().getArchiveMode(account) != ArchiveMode.server) + return; + SaveMode sessionSaveMode = sessionSaves.get(account, session); + if (sessionSaveMode == saveMode) + return; + SaveMode userSaveMode = getUserSaveMode(account, user); + if (sessionSaveMode == null) { + if (userSaveMode == saveMode) + return; + else + sendSessionUpdate(account, session, saveMode); + } else { + if (userSaveMode == saveMode) + sendSessionRemove(account, session); + else + sendSessionUpdate(account, session, saveMode); + } + } + + public boolean isModificationsSucceed(String account) { + ModificationStorage modificationStorage = modificationStorages + .get(account); + if (modificationStorage == null) + return false; + return modificationStorage.isSucceed(); + } + + @Override + public void onAccountArchiveModeChanged(AccountItem accountItem) { + availableArchiveRequestProvider.remove(accountItem.getAccount()); + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/archive/ModificationStorage.java b/app/src/main/java/com/xabber/android/data/extension/archive/ModificationStorage.java index fe8e71eda3..1b55e16c82 100644 --- a/app/src/main/java/com/xabber/android/data/extension/archive/ModificationStorage.java +++ b/app/src/main/java/com/xabber/android/data/extension/archive/ModificationStorage.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,74 +18,74 @@ public class ModificationStorage extends HeaderSequence { - /** - * Whether all headers and messages has been received. - */ - private boolean succeed; + /** + * Whether all headers and messages has been received. + */ + private boolean succeed; - /** - * Last received modification request. - */ - private Date last; + /** + * Last received modification request. + */ + private Date last; - /** - * Time stamp when request has been sent. - */ - private Date request; + /** + * Time stamp when request has been sent. + */ + private Date request; - public ModificationStorage() { - last = null; - request = null; - succeed = false; - } + public ModificationStorage() { + last = null; + request = null; + succeed = false; + } - public Date getLastRequest() { - return last; - } + public Date getLastRequest() { + return last; + } - public boolean isSucceed() { - return succeed; - } + public boolean isSucceed() { + return succeed; + } - /** - * Reset storage state and mark as in progress. - */ - public void onConnected() { - super.reset(); - super.setInProgress(true); - succeed = false; - } + /** + * Reset storage state and mark as in progress. + */ + public void onConnected() { + super.reset(); + super.setInProgress(true); + succeed = false; + } - /** - * Stores request time. - * - * @param request - * @return Whether modification request should be performed. - */ - public boolean request(Date request) { - if (last == null) { - last = request; - return false; - } else { - this.request = request; - return true; - } - } + /** + * Stores request time. + * + * @param request + * @return Whether modification request should be performed. + */ + public boolean request(Date request) { + if (last == null) { + last = request; + return false; + } else { + this.request = request; + return true; + } + } - /** - * All headers and messages has been received. - */ - public void onSuccess() { - last = request; - request = null; - succeed = true; - } + /** + * All headers and messages has been received. + */ + public void onSuccess() { + last = request; + request = null; + succeed = true; + } - /** - * Modification request station has been completed (on success or on error). - */ - public void onFinished() { - super.setInProgress(false); - } + /** + * Modification request station has been completed (on success or on error). + */ + public void onFinished() { + super.setInProgress(false); + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/archive/OnArchiveModificationsReceivedListener.java b/app/src/main/java/com/xabber/android/data/extension/archive/OnArchiveModificationsReceivedListener.java index d8b0b368d1..47bffdf5b4 100644 --- a/app/src/main/java/com/xabber/android/data/extension/archive/OnArchiveModificationsReceivedListener.java +++ b/app/src/main/java/com/xabber/android/data/extension/archive/OnArchiveModificationsReceivedListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -19,17 +19,16 @@ /** * Listen for modifications in message archive to be received. - * + * * @author alexander.ivanov - * */ public interface OnArchiveModificationsReceivedListener extends BaseManagerInterface { - /** - * Modifications has been received. - * - * @param connection - */ - void onArchiveModificationsReceived(ConnectionItem connection); + /** + * Modifications has been received. + * + * @param connection + */ + void onArchiveModificationsReceived(ConnectionItem connection); } diff --git a/app/src/main/java/com/xabber/android/data/extension/attention/AttentionManager.java b/app/src/main/java/com/xabber/android/data/extension/attention/AttentionManager.java index f995ba0c2e..d174d3819c 100644 --- a/app/src/main/java/com/xabber/android/data/extension/attention/AttentionManager.java +++ b/app/src/main/java/com/xabber/android/data/extension/attention/AttentionManager.java @@ -1,32 +1,23 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data.extension.attention; -import java.util.Iterator; - -import org.jivesoftware.smack.Connection; -import org.jivesoftware.smack.ConnectionCreationListener; -import org.jivesoftware.smack.XMPPConnection; -import org.jivesoftware.smack.packet.Message; -import org.jivesoftware.smack.packet.Packet; -import org.jivesoftware.smack.packet.PacketExtension; -import org.jivesoftware.smackx.ServiceDiscoveryManager; - import android.media.AudioManager; import android.net.Uri; +import com.xabber.android.R; import com.xabber.android.data.Application; import com.xabber.android.data.NetworkException; import com.xabber.android.data.OnLoadListener; @@ -37,6 +28,7 @@ import com.xabber.android.data.connection.ConnectionManager; import com.xabber.android.data.connection.ConnectionThread; import com.xabber.android.data.connection.OnPacketListener; +import com.xabber.android.data.entity.BaseEntity; import com.xabber.android.data.extension.capability.CapabilitiesManager; import com.xabber.android.data.extension.capability.ClientInfo; import com.xabber.android.data.message.AbstractChat; @@ -47,163 +39,171 @@ import com.xabber.android.data.notification.NotificationManager; import com.xabber.android.data.roster.PresenceManager; import com.xabber.android.data.roster.ResourceItem; -import com.xabber.androiddev.R; import com.xabber.xmpp.address.Jid; import com.xabber.xmpp.attention.Attention; +import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.ConnectionCreationListener; +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smack.packet.PacketExtension; +import org.jivesoftware.smackx.ServiceDiscoveryManager; + +import java.util.Iterator; + /** * XEP-0224: Attention. - * + * * @author alexander.ivanov - * */ public class AttentionManager implements OnPacketListener, OnLoadListener { - private final static Object enabledLock; - - private final static AttentionManager instance; - - static { - instance = new AttentionManager(); - Application.getInstance().addManager(instance); - - enabledLock = new Object(); - Connection - .addConnectionCreationListener(new ConnectionCreationListener() { - @Override - public void connectionCreated(final Connection connection) { - synchronized (enabledLock) { - if (SettingsManager.chatsAttention()) - ServiceDiscoveryManager.getInstanceFor( - connection).addFeature( - Attention.NAMESPACE); - } - } - }); - } - - public static AttentionManager getInstance() { - return instance; - } - - private final EntityNotificationProvider attentionRequestProvider = new EntityNotificationProvider( - R.drawable.ic_stat_attention) { - - @Override - public Uri getSound() { - return SettingsManager.chatsAttentionSound(); - } - - @Override - public int getStreamType() { - return AudioManager.STREAM_RING; - } - - }; - - public AttentionManager() { - } - - public void onSettingsChanged() { - synchronized (enabledLock) { - for (String account : AccountManager.getInstance().getAccounts()) { - ConnectionThread connectionThread = AccountManager - .getInstance().getAccount(account) - .getConnectionThread(); - if (connectionThread == null) - continue; - XMPPConnection xmppConnection = connectionThread - .getXMPPConnection(); - if (xmppConnection == null) - continue; - ServiceDiscoveryManager manager = ServiceDiscoveryManager - .getInstanceFor(xmppConnection); - if (manager == null) - continue; - boolean contains = false; - for (Iterator iterator = manager.getFeatures(); iterator - .hasNext();) - if (Attention.NAMESPACE.equals(iterator.next())) - contains = true; - if (SettingsManager.chatsAttention() == contains) - continue; - if (SettingsManager.chatsAttention()) - manager.addFeature(Attention.NAMESPACE); - else - manager.removeFeature(Attention.NAMESPACE); - } - AccountManager.getInstance().resendPresence(); - } - } - - @Override - public void onLoad() { - Application.getInstance().runOnUiThread(new Runnable() { - @Override - public void run() { - onLoaded(); - } - }); - } - - private void onLoaded() { - NotificationManager.getInstance().registerNotificationProvider( - attentionRequestProvider); - } - - @Override - public void onPacket(ConnectionItem connection, String bareAddress, - Packet packet) { - if (!(connection instanceof AccountItem)) - return; - if (!(packet instanceof Message)) - return; - if (!SettingsManager.chatsAttention()) - return; - final String account = ((AccountItem) connection).getAccount(); - if (bareAddress == null) - return; - for (PacketExtension packetExtension : packet.getExtensions()) - if (packetExtension instanceof Attention) { - MessageManager.getInstance().openChat(account, bareAddress); - MessageManager.getInstance() - .getOrCreateChat(account, bareAddress) - .newAction(null, null, ChatAction.attention_requested); - attentionRequestProvider.add(new AttentionRequest(account, - bareAddress), true); - } - } - - public void sendAttention(String account, String user) - throws NetworkException { - AbstractChat chat = MessageManager.getInstance().getOrCreateChat( - account, user); - if (!(chat instanceof RegularChat)) - throw new NetworkException(R.string.ENTRY_IS_NOT_FOUND); - String to = chat.getTo(); - if (Jid.getResource(to) == null || "".equals(Jid.getResource(to))) { - ResourceItem resourceItem = PresenceManager.getInstance() - .getResourceItem(account, user); - if (resourceItem == null) - throw new NetworkException(R.string.NOT_CONNECTED); - to = resourceItem.getUser(user); - } - ClientInfo clientInfo = CapabilitiesManager.getInstance() - .getClientInfo(account, to); - if (clientInfo == null) - throw new NetworkException(R.string.ENTRY_IS_NOT_AVAILABLE); - if (!clientInfo.getFeatures().contains(Attention.NAMESPACE)) - throw new NetworkException(R.string.ATTENTION_IS_NOT_SUPPORTED); - Message message = new Message(); - message.setTo(to); - message.setType(Message.Type.headline); - message.addExtension(new Attention()); - ConnectionManager.getInstance().sendPacket(account, message); - chat.newAction(null, null, ChatAction.attention_called); - } - - public void removeAccountNotifications(String account, String user) { - attentionRequestProvider.remove(account, user); - } + private final static Object enabledLock; + + private final static AttentionManager instance; + + static { + instance = new AttentionManager(); + Application.getInstance().addManager(instance); + + enabledLock = new Object(); + Connection + .addConnectionCreationListener(new ConnectionCreationListener() { + @Override + public void connectionCreated(final Connection connection) { + synchronized (enabledLock) { + if (SettingsManager.chatsAttention()) + ServiceDiscoveryManager.getInstanceFor( + connection).addFeature( + Attention.NAMESPACE); + } + } + }); + } + + private final EntityNotificationProvider attentionRequestProvider = new EntityNotificationProvider( + R.drawable.ic_stat_error) { + + @Override + public Uri getSound() { + return SettingsManager.chatsAttentionSound(); + } + + @Override + public int getStreamType() { + return AudioManager.STREAM_RING; + } + + }; + + public AttentionManager() { + } + + public static AttentionManager getInstance() { + return instance; + } + + public void onSettingsChanged() { + synchronized (enabledLock) { + for (String account : AccountManager.getInstance().getAccounts()) { + ConnectionThread connectionThread = AccountManager + .getInstance().getAccount(account) + .getConnectionThread(); + if (connectionThread == null) + continue; + XMPPConnection xmppConnection = connectionThread + .getXMPPConnection(); + if (xmppConnection == null) + continue; + ServiceDiscoveryManager manager = ServiceDiscoveryManager + .getInstanceFor(xmppConnection); + if (manager == null) + continue; + boolean contains = false; + for (Iterator iterator = manager.getFeatures(); iterator + .hasNext(); ) + if (Attention.NAMESPACE.equals(iterator.next())) + contains = true; + if (SettingsManager.chatsAttention() == contains) + continue; + if (SettingsManager.chatsAttention()) + manager.addFeature(Attention.NAMESPACE); + else + manager.removeFeature(Attention.NAMESPACE); + } + AccountManager.getInstance().resendPresence(); + } + } + + @Override + public void onLoad() { + Application.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + onLoaded(); + } + }); + } + + private void onLoaded() { + NotificationManager.getInstance().registerNotificationProvider( + attentionRequestProvider); + } + + @Override + public void onPacket(ConnectionItem connection, String bareAddress, + Packet packet) { + if (!(connection instanceof AccountItem)) + return; + if (!(packet instanceof Message)) + return; + if (!SettingsManager.chatsAttention()) + return; + final String account = ((AccountItem) connection).getAccount(); + if (bareAddress == null) + return; + for (PacketExtension packetExtension : packet.getExtensions()) + if (packetExtension instanceof Attention) { + MessageManager.getInstance().openChat(account, bareAddress); + MessageManager.getInstance() + .getOrCreateChat(account, bareAddress) + .newAction(null, null, ChatAction.attention_requested); + attentionRequestProvider.add(new AttentionRequest(account, + bareAddress), true); + } + } + + public void sendAttention(String account, String user) + throws NetworkException { + AbstractChat chat = MessageManager.getInstance().getOrCreateChat( + account, user); + if (!(chat instanceof RegularChat)) + throw new NetworkException(R.string.ENTRY_IS_NOT_FOUND); + String to = chat.getTo(); + if (Jid.getResource(to) == null || "".equals(Jid.getResource(to))) { + ResourceItem resourceItem = PresenceManager.getInstance() + .getResourceItem(account, user); + if (resourceItem == null) + throw new NetworkException(R.string.NOT_CONNECTED); + to = resourceItem.getUser(user); + } + ClientInfo clientInfo = CapabilitiesManager.getInstance() + .getClientInfo(account, to); + if (clientInfo == null) + throw new NetworkException(R.string.ENTRY_IS_NOT_AVAILABLE); + if (!clientInfo.getFeatures().contains(Attention.NAMESPACE)) + throw new NetworkException(R.string.ATTENTION_IS_NOT_SUPPORTED); + Message message = new Message(); + message.setTo(to); + message.setType(Message.Type.headline); + message.addExtension(new Attention()); + ConnectionManager.getInstance().sendPacket(account, message); + chat.newAction(null, null, ChatAction.attention_called); + } + + public void removeAccountNotifications(BaseEntity chat) { + attentionRequestProvider.remove(chat.getAccount(), chat.getUser()); + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/attention/AttentionRequest.java b/app/src/main/java/com/xabber/android/data/extension/attention/AttentionRequest.java index 3e49015acc..31e20d5dbb 100644 --- a/app/src/main/java/com/xabber/android/data/extension/attention/AttentionRequest.java +++ b/app/src/main/java/com/xabber/android/data/extension/attention/AttentionRequest.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,35 +16,35 @@ import android.content.Intent; +import com.xabber.android.R; import com.xabber.android.data.Application; import com.xabber.android.data.entity.BaseEntity; import com.xabber.android.data.notification.EntityNotificationItem; import com.xabber.android.data.roster.RosterManager; import com.xabber.android.ui.ChatViewer; -import com.xabber.androiddev.R; public class AttentionRequest extends BaseEntity implements - EntityNotificationItem { - - public AttentionRequest(String account, String user) { - super(account, user); - } - - @Override - public Intent getIntent() { - return ChatViewer.createAttentionRequestIntent( - Application.getInstance(), account, user); - } - - @Override - public String getTitle() { - return RosterManager.getInstance().getBestContact(account, user) - .getName(); - } - - @Override - public String getText() { - return Application.getInstance().getString(R.string.pay_attention); - } + EntityNotificationItem { + + public AttentionRequest(String account, String user) { + super(account, user); + } + + @Override + public Intent getIntent() { + return ChatViewer.createAttentionRequestIntent( + Application.getInstance(), account, user); + } + + @Override + public String getTitle() { + return RosterManager.getInstance().getBestContact(account, user) + .getName(); + } + + @Override + public String getText() { + return Application.getInstance().getString(R.string.pay_attention); + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/avatar/AccountAvatarSet.java b/app/src/main/java/com/xabber/android/data/extension/avatar/AccountAvatarSet.java deleted file mode 100644 index e9964da4bf..0000000000 --- a/app/src/main/java/com/xabber/android/data/extension/avatar/AccountAvatarSet.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.xabber.android.data.extension.avatar; - -import com.xabber.android.data.Application; -import com.xabber.android.data.account.AccountManager; - -/** - * Set of default account's avatars. - * - * @author alexander.ivanov - * - */ -public class AccountAvatarSet extends BaseAvatarSet { - - public AccountAvatarSet(Application application, int array, - int defaultDrawable) { - super(application, array, defaultDrawable); - } - - @Override - protected int getIndex(String user) { - return AccountManager.getInstance().getColorLevel(user); - } -} diff --git a/app/src/main/java/com/xabber/android/data/extension/avatar/AvatarManager.java b/app/src/main/java/com/xabber/android/data/extension/avatar/AvatarManager.java index 89a7587f3a..27ab21bd6a 100644 --- a/app/src/main/java/com/xabber/android/data/extension/avatar/AvatarManager.java +++ b/app/src/main/java/com/xabber/android/data/extension/avatar/AvatarManager.java @@ -1,490 +1,508 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data.extension.avatar; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; - -import org.jivesoftware.smack.packet.Packet; -import org.jivesoftware.smack.packet.PacketExtension; -import org.jivesoftware.smack.packet.Presence; - import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.graphics.Canvas; import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; +import com.xabber.android.R; import com.xabber.android.data.Application; import com.xabber.android.data.OnLoadListener; import com.xabber.android.data.OnLowMemoryListener; import com.xabber.android.data.SettingsManager; import com.xabber.android.data.account.AccountItem; +import com.xabber.android.data.account.AccountManager; import com.xabber.android.data.account.OAuthManager; import com.xabber.android.data.connection.ConnectionItem; import com.xabber.android.data.connection.OnPacketListener; import com.xabber.android.data.extension.vcard.VCardManager; -import com.xabber.androiddev.R; import com.xabber.xmpp.address.Jid; import com.xabber.xmpp.avatar.VCardUpdate; +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smack.packet.PacketExtension; +import org.jivesoftware.smack.packet.Presence; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + /** * Provides information about avatars (hashes and values). Store and retrieve * hashes from database and binary values from file system. Caches user's hashes * and avatar's values in memory. Handles changes in user's hashes. Requests * information from server when avatar for given hash don't exists locally. - * - *

+ *

+ *

* This class is thread safe. All operation modification made from synchronized * blocks. - * - *

+ *

+ *

* All requests to database / file system made in background thread or on * application load. - * + * * @author alexander.ivanov */ -public class AvatarManager implements OnLoadListener, OnLowMemoryListener, - OnPacketListener { - - /** - * Maximum image width / height to be loaded. - */ - private static final int MAX_SIZE = 256; - - private static final String EMPTY_HASH = ""; - private static final Bitmap EMPTY_BITMAP = Bitmap.createBitmap(1, 1, - Bitmap.Config.ALPHA_8); - - private final Application application; - - /** - * Map with hashes for specified users. - * - * {@link #EMPTY_HASH} is used to store null values. - */ - private final Map hashes; - - /** - * Map with bitmaps for specified hashes. - * - * {@link #EMPTY_BITMAP} is used to store null values. - */ - private final Map bitmaps; - - /** - * Map with drawable used in contact list only for specified uses. - */ - private final Map contactListDrawables; - - /** - * Users' default avatar set. - */ - private final BaseAvatarSet userAvatarSet; - - /** - * Accounts' default avatar set. - */ - private final AccountAvatarSet accountAvatarSet; - - /** - * Rooms' default avatar set. - */ - private final BaseAvatarSet roomAvatarSet; - - private final static AvatarManager instance; - - static { - instance = new AvatarManager(); - Application.getInstance().addManager(instance); - } - - public static AvatarManager getInstance() { - return instance; - } - - private AvatarManager() { - this.application = Application.getInstance(); - userAvatarSet = new BaseAvatarSet(application, R.array.default_avatars, - R.drawable.avatar_1_1); - accountAvatarSet = new AccountAvatarSet(application, - R.array.account_avatars, R.drawable.avatar_account_1); - roomAvatarSet = new BaseAvatarSet(application, R.array.muc_avatars, - R.drawable.avatar_muc_1); - hashes = new HashMap(); - bitmaps = new HashMap(); - contactListDrawables = new HashMap(); - } - - @Override - public void onLoad() { - final Map hashes = new HashMap(); - final Map bitmaps = new HashMap(); - Cursor cursor = AvatarTable.getInstance().list(); - try { - if (cursor.moveToFirst()) { - do { - String hash = AvatarTable.getHash(cursor); - hashes.put(AvatarTable.getUser(cursor), - hash == null ? EMPTY_HASH : hash); - } while (cursor.moveToNext()); - } - } finally { - cursor.close(); - } - for (String hash : new HashSet(hashes.values())) - if (hash != EMPTY_HASH) { - Bitmap bitmap = makeBitemap(AvatarStorage.getInstance().read( - hash)); - bitmaps.put(hash, bitmap == null ? EMPTY_BITMAP : bitmap); - } - Application.getInstance().runOnUiThread(new Runnable() { - @Override - public void run() { - onLoaded(hashes, bitmaps); - }; - }); - } - - private void onLoaded(Map hashes, - Map bitmaps) { - this.hashes.putAll(hashes); - this.bitmaps.putAll(bitmaps); - } - - /** - * Sets avatar's hash for user. - * - * @param bareAddress - * @param hash - * can be null. - */ - private void setHash(final String bareAddress, final String hash) { - hashes.put(bareAddress, hash == null ? EMPTY_HASH : hash); - contactListDrawables.remove(bareAddress); - application.runInBackground(new Runnable() { - @Override - public void run() { - AvatarTable.getInstance().write(bareAddress, hash); - } - }); - } - - /** - * Make {@link Bitmap} from array of bytes. - * - * @param value - * @return Bitmap. null can be returned if value is invalid or - * is null. - */ - private static Bitmap makeBitemap(byte[] value) { - if (value == null) - return null; - - // Load only size values - BitmapFactory.Options sizeOptions = new BitmapFactory.Options(); - sizeOptions.inJustDecodeBounds = true; - BitmapFactory.decodeByteArray(value, 0, value.length, sizeOptions); - - // Calculate factor to down scale image - int scale = 1; - int width_tmp = sizeOptions.outWidth; - int height_tmp = sizeOptions.outHeight; - while (width_tmp / 2 >= MAX_SIZE && height_tmp / 2 >= MAX_SIZE) { - scale *= 2; - width_tmp /= 2; - height_tmp /= 2; - } - - // Load image - BitmapFactory.Options resultOptions = new BitmapFactory.Options(); - resultOptions.inSampleSize = scale; - return BitmapFactory.decodeByteArray(value, 0, value.length, - resultOptions); - } - - /** - * Get avatar's value for user. - * - * @param bareAddress - * @return avatar's value. null can be returned if user has no - * avatar or avatar doesn't exists. - */ - private Bitmap getBitmap(String bareAddress) { - String hash = hashes.get(bareAddress); - if (hash == null || hash == EMPTY_HASH) - return null; - Bitmap bitmap = bitmaps.get(hash); - if (bitmap == EMPTY_BITMAP) - return null; - else - return bitmap; - } - - /** - * Sets avatar's value. - * - * @param hash - * @param value - */ - private void setValue(final String hash, final byte[] value) { - if (hash == null) - return; - Bitmap bitmap = makeBitemap(value); - bitmaps.put(hash, bitmap == null ? EMPTY_BITMAP : bitmap); - application.runInBackground(new Runnable() { - @Override - public void run() { - AvatarStorage.getInstance().write(hash, value); - } - }); - } - - @Override - public void onLowMemory() { - contactListDrawables.clear(); - userAvatarSet.onLowMemory(); - accountAvatarSet.onLowMemory(); - roomAvatarSet.onLowMemory(); - } - - /** - * Gets account's avatar. - * - * @param account - * @return Avatar or default avatar if: - *

    - *
  • account has no avatar.
  • - *
- */ - public Drawable getAccountAvatar(String account) { - String jid = OAuthManager.getInstance().getAssignedJid(account); - if (jid == null) - jid = account; - Bitmap value = getBitmap(Jid.getBareAddress(jid)); - if (value != null) - return new BitmapDrawable(value); - else - return application.getResources().getDrawable( - accountAvatarSet.getResourceId(account)); - } - - /** - * Gets avatar for regular user. - * - * @param user - * @return - */ - public Drawable getUserAvatar(String user) { - Bitmap value = getBitmap(user); - if (value != null) - return new BitmapDrawable(value); - else - return application.getResources().getDrawable( - userAvatarSet.getResourceId(user)); - } - - /** - * Gets bitmap with avatar for regular user. - * - * @param user - * @return - */ - public Bitmap getUserBitmap(String user) { - Bitmap value = getBitmap(user); - if (value != null) - return value; - else - return ((BitmapDrawable) application.getResources().getDrawable( - userAvatarSet.getResourceId(user))).getBitmap(); - } - - /** - * Gets and caches drawable with avatar for regular user. - * - * @param user - * @return - */ - public Drawable getUserAvatarForContactList(String user) { - Drawable drawable = contactListDrawables.get(user); - if (drawable == null) { - drawable = getUserAvatar(user); - contactListDrawables.put(user, drawable); - } - return drawable; - } - - /** - * Gets avatar for the room. - * - * @param user - * @return - */ - public Drawable getRoomAvatar(String user) { - return application.getResources().getDrawable( - roomAvatarSet.getResourceId(user)); - } - - /** - * Gets bitmap for the room. - * - * @param user - * @return - */ - public Bitmap getRoomBitmap(String user) { - return ((BitmapDrawable) getRoomAvatar(user)).getBitmap(); - } - - /** - * Gets and caches drawable with room's avatar. - * - * @param user - * @return - */ - public Drawable getRoomAvatarForContactList(String user) { - Drawable drawable = contactListDrawables.get(user); - if (drawable == null) { - drawable = getRoomAvatar(user); - contactListDrawables.put(user, drawable); - } - return drawable; - } - - /** - * Gets avatar for occupant in the room. - * - * @param user - * @return - */ - public Drawable getOccupantAvatar(String user) { - return application.getResources().getDrawable( - userAvatarSet.getResourceId(user)); - } - - /** - * Avatar was received. - * - * @param bareAddress - * @param hash - * @param value - */ - public void onAvatarReceived(String bareAddress, String hash, byte[] value) { - setValue(hash, value); - setHash(bareAddress, hash); - } - - @Override - public void onPacket(ConnectionItem connection, String bareAddress, - Packet packet) { - if (!(packet instanceof Presence) || bareAddress == null) - return; - if (!(connection instanceof AccountItem)) - return; - String account = ((AccountItem) connection).getAccount(); - Presence presence = (Presence) packet; - if (presence.getType() == Presence.Type.error) - return; - for (PacketExtension packetExtension : presence.getExtensions()) - if (packetExtension instanceof VCardUpdate) { - VCardUpdate vCardUpdate = (VCardUpdate) packetExtension; - if (vCardUpdate.isValid() && vCardUpdate.isPhotoReady()) - onPhotoReady(account, bareAddress, vCardUpdate); - } - } - - private void onPhotoReady(final String account, final String bareAddress, - VCardUpdate vCardUpdate) { - if (vCardUpdate.isEmpty()) { - setHash(bareAddress, null); - return; - } - final String hash = vCardUpdate.getPhotoHash(); - if (bitmaps.containsKey(hash)) { - setHash(bareAddress, hash); - return; - } - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - loadBitmap(account, bareAddress, hash); - } - }); - } - - /** - * Read bitmap in background. - * - * @param account - * @param bareAddress - * @param hash - */ - private void loadBitmap(final String account, final String bareAddress, - final String hash) { - final byte[] value = AvatarStorage.getInstance().read(hash); - final Bitmap bitmap = makeBitemap(value); - Application.getInstance().runOnUiThread(new Runnable() { - @Override - public void run() { - onBitmapLoaded(account, bareAddress, hash, value, bitmap); - } - }); - } - - /** - * Update data or request avatar on bitmap load. - * - * @param account - * @param bareAddress - * @param hash - * @param value - * @param bitmap - */ - private void onBitmapLoaded(String account, String bareAddress, - String hash, byte[] value, Bitmap bitmap) { - if (value == null) { - if (SettingsManager.connectionLoadVCard()) - VCardManager.getInstance().request(account, bareAddress, hash); - } else { - bitmaps.put(hash, bitmap == null ? EMPTY_BITMAP : bitmap); - setHash(bareAddress, hash); - } - } - - /** - * @param bitmap - * @return Scaled bitmap to be used for shortcut. - */ - public Bitmap createShortcutBitmap(Bitmap bitmap) { - int size = getLauncherLargeIconSize(); - int max = Math.max(bitmap.getWidth(), bitmap.getHeight()); - if (max == size) - return bitmap; - double scale = ((double) size) / max; - int width = (int) (bitmap.getWidth() * scale); - int height = (int) (bitmap.getHeight() * scale); - return Bitmap.createScaledBitmap(bitmap, width, height, true); - } - - private int getLauncherLargeIconSize() { - if (Application.SDK_INT < 9) - return BaseShortcutHelper.getLauncherLargeIconSize(); - else if (Application.SDK_INT < 11) - return GingerbreadShortcutHelper.getLauncherLargeIconSize(); - else - return HoneycombShortcutHelper.getLauncherLargeIconSize(); - } +public class AvatarManager implements OnLoadListener, OnLowMemoryListener, OnPacketListener { + + /** + * Maximum image width / height to be loaded. + */ + private static final int MAX_SIZE = 256; + + private static final String EMPTY_HASH = ""; + private static final Bitmap EMPTY_BITMAP = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8); + private final static AvatarManager instance; + + static { + instance = new AvatarManager(); + Application.getInstance().addManager(instance); + } + + private final Application application; + /** + * Map with hashes for specified users. + *

+ * {@link #EMPTY_HASH} is used to store null values. + */ + private final Map hashes; + /** + * Map with bitmaps for specified hashes. + *

+ * {@link #EMPTY_BITMAP} is used to store null values. + */ + private final Map bitmaps; + /** + * Map with drawable used in contact list only for specified uses. + */ + private final Map contactListDrawables; + /** + * Users' default avatar set. + */ + private final BaseAvatarSet userAvatarSet; + /** + * Rooms' default avatar set. + */ + private final BaseAvatarSet roomAvatarSet; + private final int[] accountColors; + + private AvatarManager() { + this.application = Application.getInstance(); + userAvatarSet = new BaseAvatarSet(application, R.array.default_avatars_icons, R.array.default_avatars_colors); + roomAvatarSet = new BaseAvatarSet(application, R.array.muc_avatars, R.array.default_avatars_colors); + + accountColors = application.getResources().getIntArray(R.array.account_action_bar); + + hashes = new HashMap<>(); + bitmaps = new HashMap<>(); + contactListDrawables = new HashMap<>(); + } + + public static AvatarManager getInstance() { + return instance; + } + + /** + * Make {@link Bitmap} from array of bytes. + * + * @param value + * @return Bitmap. null can be returned if value is invalid or + * is null. + */ + private static Bitmap makeBitmap(byte[] value) { + if (value == null) { + return null; + } + + // Load only size values + BitmapFactory.Options sizeOptions = new BitmapFactory.Options(); + sizeOptions.inJustDecodeBounds = true; + BitmapFactory.decodeByteArray(value, 0, value.length, sizeOptions); + + // Calculate factor to down scale image + int scale = 1; + int width_tmp = sizeOptions.outWidth; + int height_tmp = sizeOptions.outHeight; + while (width_tmp / 2 >= MAX_SIZE && height_tmp / 2 >= MAX_SIZE) { + scale *= 2; + width_tmp /= 2; + height_tmp /= 2; + } + + // Load image + BitmapFactory.Options resultOptions = new BitmapFactory.Options(); + resultOptions.inSampleSize = scale; + return BitmapFactory.decodeByteArray(value, 0, value.length, resultOptions); + } + + public static Bitmap drawableToBitmap(Drawable drawable) { + if (drawable instanceof BitmapDrawable) { + return ((BitmapDrawable) drawable).getBitmap(); + } + + int width = drawable.getIntrinsicWidth(); + width = width > 0 ? width : 1; + int height = drawable.getIntrinsicHeight(); + height = height > 0 ? height : 1; + + Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + drawable.draw(canvas); + + return bitmap; + } + + @Override + public void onLoad() { + final Map hashes = new HashMap<>(); + final Map bitmaps = new HashMap<>(); + Cursor cursor = AvatarTable.getInstance().list(); + try { + if (cursor.moveToFirst()) { + do { + String hash = AvatarTable.getHash(cursor); + hashes.put(AvatarTable.getUser(cursor), hash == null ? EMPTY_HASH : hash); + } while (cursor.moveToNext()); + } + } finally { + cursor.close(); + } + for (String hash : new HashSet<>(hashes.values())) + if (!hash.equals(EMPTY_HASH)) { + Bitmap bitmap = makeBitmap(AvatarStorage.getInstance().read(hash)); + bitmaps.put(hash, bitmap == null ? EMPTY_BITMAP : bitmap); + } + Application.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + onLoaded(hashes, bitmaps); + } + }); + } + + private void onLoaded(Map hashes, Map bitmaps) { + this.hashes.putAll(hashes); + this.bitmaps.putAll(bitmaps); + } + + /** + * Sets avatar's hash for user. + * + * @param bareAddress + * @param hash can be null. + */ + private void setHash(final String bareAddress, final String hash) { + hashes.put(bareAddress, hash == null ? EMPTY_HASH : hash); + contactListDrawables.remove(bareAddress); + application.runInBackground(new Runnable() { + @Override + public void run() { + AvatarTable.getInstance().write(bareAddress, hash); + } + }); + } + + /** + * Get avatar's value for user. + * + * @param bareAddress + * @return avatar's value. null can be returned if user has no + * avatar or avatar doesn't exists. + */ + private Bitmap getBitmap(String bareAddress) { + String hash = hashes.get(bareAddress); + if (hash == null || hash.equals(EMPTY_HASH)) { + return null; + } + Bitmap bitmap = bitmaps.get(hash); + if (bitmap == EMPTY_BITMAP) { + return null; + } else { + return bitmap; + } + } + + /** + * Sets avatar's value. + * + * @param hash + * @param value + */ + private void setValue(final String hash, final byte[] value) { + if (hash == null) { + return; + } + Bitmap bitmap = makeBitmap(value); + bitmaps.put(hash, bitmap == null ? EMPTY_BITMAP : bitmap); + application.runInBackground(new Runnable() { + @Override + public void run() { + AvatarStorage.getInstance().write(hash, value); + } + }); + } + + @Override + public void onLowMemory() { + contactListDrawables.clear(); + userAvatarSet.onLowMemory(); + roomAvatarSet.onLowMemory(); + } + + /** + * Gets account's avatar. + * + * @param account + * @return Avatar or default avatar if: + *

    + *
  • account has no avatar.
  • + *
+ */ + public Drawable getAccountAvatar(String account) { + String jid = OAuthManager.getInstance().getAssignedJid(account); + if (jid == null) { + jid = account; + } + Bitmap value = getBitmap(Jid.getBareAddress(jid)); + if (value != null) { + return new BitmapDrawable(application.getResources(), value); + } else { + Drawable[] layers = new Drawable[2]; + layers[0] = new ColorDrawable(accountColors[AccountManager.getInstance().getColorLevel(account)]); + layers[1] = application.getResources().getDrawable(R.drawable.ic_avatar_1); + return new LayerDrawable(layers); + } + } + + /** + * Gets avatar for regular user. + * + * @param user + * @return + */ + public Drawable getUserAvatar(String user) { + Bitmap value = getBitmap(user); + if (value != null) { + return new BitmapDrawable(application.getResources(), value); + } else { + return getDefaultAvatarDrawable(userAvatarSet.getResourceId(user)); + } + } + + private Drawable getDefaultAvatarDrawable(BaseAvatarSet.DefaultAvatar defaultAvatar) { + Drawable[] layers = new Drawable[2]; + layers[0] = new ColorDrawable(defaultAvatar.getBackgroundColor()); + layers[1] = application.getResources().getDrawable(defaultAvatar.getIconResource()); + + + return new LayerDrawable(layers); + } + + /** + * Gets bitmap with avatar for regular user. + * + * @param user + * @return + */ + public Bitmap getUserBitmap(String user) { + Bitmap value = getBitmap(user); + if (value != null) { + return value; + } else { + return drawableToBitmap(getDefaultAvatarDrawable(userAvatarSet.getResourceId(user))); + } + } + + /** + * Gets and caches drawable with avatar for regular user. + * + * @param user + * @return + */ + public Drawable getUserAvatarForContactList(String user) { + Drawable drawable = contactListDrawables.get(user); + if (drawable == null) { + drawable = getUserAvatar(user); + contactListDrawables.put(user, drawable); + } + return drawable; + } + + /** + * Gets avatar for the room. + * + * @param user + * @return + */ + public Drawable getRoomAvatar(String user) { + return getDefaultAvatarDrawable(roomAvatarSet.getResourceId(user)); + } + + /** + * Gets bitmap for the room. + * + * @param user + * @return + */ + public Bitmap getRoomBitmap(String user) { + return drawableToBitmap(getRoomAvatar(user)); + } + + /** + * Gets and caches drawable with room's avatar. + * + * @param user + * @return + */ + public Drawable getRoomAvatarForContactList(String user) { + Drawable drawable = contactListDrawables.get(user); + if (drawable == null) { + drawable = getRoomAvatar(user); + contactListDrawables.put(user, drawable); + } + return drawable; + } + + /** + * Gets avatar for occupant in the room. + * + * @param user + * @return + */ + public Drawable getOccupantAvatar(String user) { + return getDefaultAvatarDrawable(userAvatarSet.getResourceId(user)); + } + + /** + * Avatar was received. + * + * @param bareAddress + * @param hash + * @param value + */ + public void onAvatarReceived(String bareAddress, String hash, byte[] value) { + setValue(hash, value); + setHash(bareAddress, hash); + } + + @Override + public void onPacket(ConnectionItem connection, String bareAddress, + Packet packet) { + if (!(packet instanceof Presence) || bareAddress == null) { + return; + } + if (!(connection instanceof AccountItem)) { + return; + } + String account = ((AccountItem) connection).getAccount(); + Presence presence = (Presence) packet; + if (presence.getType() == Presence.Type.error) { + return; + } + for (PacketExtension packetExtension : presence.getExtensions()) { + if (packetExtension instanceof VCardUpdate) { + VCardUpdate vCardUpdate = (VCardUpdate) packetExtension; + if (vCardUpdate.isValid() && vCardUpdate.isPhotoReady()) { + onPhotoReady(account, bareAddress, vCardUpdate); + } + } + } + } + + private void onPhotoReady(final String account, final String bareAddress, VCardUpdate vCardUpdate) { + if (vCardUpdate.isEmpty()) { + setHash(bareAddress, null); + return; + } + final String hash = vCardUpdate.getPhotoHash(); + if (bitmaps.containsKey(hash)) { + setHash(bareAddress, hash); + return; + } + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + loadBitmap(account, bareAddress, hash); + } + }); + } + + /** + * Read bitmap in background. + * + * @param account + * @param bareAddress + * @param hash + */ + private void loadBitmap(final String account, final String bareAddress, final String hash) { + final byte[] value = AvatarStorage.getInstance().read(hash); + final Bitmap bitmap = makeBitmap(value); + Application.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + onBitmapLoaded(account, bareAddress, hash, value, bitmap); + } + }); + } + + /** + * Update data or request avatar on bitmap load. + * + * @param account + * @param bareAddress + * @param hash + * @param value + * @param bitmap + */ + private void onBitmapLoaded(String account, String bareAddress, + String hash, byte[] value, Bitmap bitmap) { + if (value == null) { + if (SettingsManager.connectionLoadVCard()) { + VCardManager.getInstance().request(account, bareAddress, hash); + } + } else { + bitmaps.put(hash, bitmap == null ? EMPTY_BITMAP : bitmap); + setHash(bareAddress, hash); + } + } + + /** + * @param bitmap + * @return Scaled bitmap to be used for shortcut. + */ + public Bitmap createShortcutBitmap(Bitmap bitmap) { + int size = getLauncherLargeIconSize(); + int max = Math.max(bitmap.getWidth(), bitmap.getHeight()); + if (max == size) { + return bitmap; + } + double scale = ((double) size) / max; + int width = (int) (bitmap.getWidth() * scale); + int height = (int) (bitmap.getHeight() * scale); + return Bitmap.createScaledBitmap(bitmap, width, height, true); + } + + private int getLauncherLargeIconSize() { + return HoneycombShortcutHelper.getLauncherLargeIconSize(); + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/avatar/AvatarStorage.java b/app/src/main/java/com/xabber/android/data/extension/avatar/AvatarStorage.java index 8695f5cfb9..7926a001b3 100644 --- a/app/src/main/java/com/xabber/android/data/extension/avatar/AvatarStorage.java +++ b/app/src/main/java/com/xabber/android/data/extension/avatar/AvatarStorage.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -27,69 +27,69 @@ /** * Manager for avatar files. - * + * * @author alexander.ivanov */ class AvatarStorage implements OnLoadListener, OnClearListener { - private final File folder; + private final File folder; - private static AvatarStorage instance; + private static AvatarStorage instance; - static { - instance = new AvatarStorage(Application.getInstance()); - Application.getInstance().addManager(instance); - } + static { + instance = new AvatarStorage(Application.getInstance()); + Application.getInstance().addManager(instance); + } - public static AvatarStorage getInstance() { - return instance; - } + public static AvatarStorage getInstance() { + return instance; + } - private AvatarStorage(Application application) { - folder = new File(application.getFilesDir(), "avatars"); - } + private AvatarStorage(Application application) { + folder = new File(application.getFilesDir(), "avatars"); + } - @Override - public void onLoad() { - if (!folder.exists()) - folder.mkdirs(); - } + @Override + public void onLoad() { + if (!folder.exists()) + folder.mkdirs(); + } - private File getFile(String hash) { - return new File(folder, hash); - } + private File getFile(String hash) { + return new File(folder, hash); + } - byte[] read(String hash) { - byte[] value; - FileInputStream inputStream; - try { - inputStream = new FileInputStream(getFile(hash)); - value = new byte[inputStream.available()]; - inputStream.read(value); - inputStream.close(); - } catch (FileNotFoundException e) { - value = null; - } catch (IOException e) { - value = null; - } - return value; - } + byte[] read(String hash) { + byte[] value; + FileInputStream inputStream; + try { + inputStream = new FileInputStream(getFile(hash)); + value = new byte[inputStream.available()]; + inputStream.read(value); + inputStream.close(); + } catch (FileNotFoundException e) { + value = null; + } catch (IOException e) { + value = null; + } + return value; + } - void write(String hash, byte[] value) { - try { - FileOutputStream outputStream = new FileOutputStream(getFile(hash)); - outputStream.write(value); - outputStream.close(); - } catch (FileNotFoundException e) { - LogManager.exception(this, e); - } catch (IOException e) { - LogManager.exception(this, e); - } - } + void write(String hash, byte[] value) { + try { + FileOutputStream outputStream = new FileOutputStream(getFile(hash)); + outputStream.write(value); + outputStream.close(); + } catch (FileNotFoundException e) { + LogManager.exception(this, e); + } catch (IOException e) { + LogManager.exception(this, e); + } + } - @Override - public void onClear() { - for (File file : folder.listFiles()) - file.delete(); - } + @Override + public void onClear() { + for (File file : folder.listFiles()) + file.delete(); + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/avatar/AvatarTable.java b/app/src/main/java/com/xabber/android/data/extension/avatar/AvatarTable.java index f0af5ac62d..e9d21677cd 100644 --- a/app/src/main/java/com/xabber/android/data/extension/avatar/AvatarTable.java +++ b/app/src/main/java/com/xabber/android/data/extension/avatar/AvatarTable.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -24,104 +24,104 @@ /** * Storage with avatar hashes for the users. - * + * * @author alexander.ivanov */ class AvatarTable extends AbstractTable { - private static final class Fields implements BaseColumns { - private Fields() { - } - - public static final String USER = "user"; - public static final String HASH = "hash"; - } - - private static final String NAME = "avatars"; - private static final String[] PROJECTION = new String[] { Fields.USER, - Fields.HASH, }; - - private final DatabaseManager databaseManager; - private SQLiteStatement writeStatement; - private final Object writeLock; - - private final static AvatarTable instance; - - static { - instance = new AvatarTable(DatabaseManager.getInstance()); - DatabaseManager.getInstance().addTable(instance); - } - - public static AvatarTable getInstance() { - return instance; - } - - private AvatarTable(DatabaseManager databaseManager) { - this.databaseManager = databaseManager; - writeStatement = null; - writeLock = new Object(); - } - - @Override - public void create(SQLiteDatabase db) { - String sql = "CREATE TABLE " + NAME + " (" + Fields.USER - + " TEXT PRIMARY KEY," + Fields.HASH + " TEXT);"; - DatabaseManager.execSQL(db, sql); - } - - @Override - public void migrate(SQLiteDatabase db, int toVersion) { - super.migrate(db, toVersion); - switch (toVersion) { - case 2: - String sql = "CREATE TABLE avatars (" + "user TEXT PRIMARY KEY," - + "hash TEXT);"; - DatabaseManager.execSQL(db, sql); - break; - default: - break; - } - } - - /** - * Saves avatar's hash for user. - * - * @param bareAddress - * @param hash - */ - void write(String bareAddress, String hash) { - synchronized (writeLock) { - if (writeStatement == null) { - SQLiteDatabase db = databaseManager.getWritableDatabase(); - writeStatement = db.compileStatement("INSERT OR REPLACE INTO " - + NAME + " (" + Fields.USER + ", " + Fields.HASH - + ") VALUES (?, ?);"); - } - writeStatement.bindString(1, bareAddress); - if (hash == null) - writeStatement.bindNull(2); - else - writeStatement.bindString(2, hash); - writeStatement.execute(); - } - } - - @Override - protected String getTableName() { - return NAME; - } - - @Override - protected String[] getProjection() { - return PROJECTION; - } - - static String getUser(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.USER)); - } - - static String getHash(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.HASH)); - } + private static final class Fields implements BaseColumns { + private Fields() { + } + + public static final String USER = "user"; + public static final String HASH = "hash"; + } + + private static final String NAME = "avatars"; + private static final String[] PROJECTION = new String[]{Fields.USER, + Fields.HASH,}; + + private final DatabaseManager databaseManager; + private SQLiteStatement writeStatement; + private final Object writeLock; + + private final static AvatarTable instance; + + static { + instance = new AvatarTable(DatabaseManager.getInstance()); + DatabaseManager.getInstance().addTable(instance); + } + + public static AvatarTable getInstance() { + return instance; + } + + private AvatarTable(DatabaseManager databaseManager) { + this.databaseManager = databaseManager; + writeStatement = null; + writeLock = new Object(); + } + + @Override + public void create(SQLiteDatabase db) { + String sql = "CREATE TABLE " + NAME + " (" + Fields.USER + + " TEXT PRIMARY KEY," + Fields.HASH + " TEXT);"; + DatabaseManager.execSQL(db, sql); + } + + @Override + public void migrate(SQLiteDatabase db, int toVersion) { + super.migrate(db, toVersion); + switch (toVersion) { + case 2: + String sql = "CREATE TABLE avatars (" + "user TEXT PRIMARY KEY," + + "hash TEXT);"; + DatabaseManager.execSQL(db, sql); + break; + default: + break; + } + } + + /** + * Saves avatar's hash for user. + * + * @param bareAddress + * @param hash + */ + void write(String bareAddress, String hash) { + synchronized (writeLock) { + if (writeStatement == null) { + SQLiteDatabase db = databaseManager.getWritableDatabase(); + writeStatement = db.compileStatement("INSERT OR REPLACE INTO " + + NAME + " (" + Fields.USER + ", " + Fields.HASH + + ") VALUES (?, ?);"); + } + writeStatement.bindString(1, bareAddress); + if (hash == null) + writeStatement.bindNull(2); + else + writeStatement.bindString(2, hash); + writeStatement.execute(); + } + } + + @Override + protected String getTableName() { + return NAME; + } + + @Override + protected String[] getProjection() { + return PROJECTION; + } + + static String getUser(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.USER)); + } + + static String getHash(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.HASH)); + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/data/extension/avatar/BaseAvatarSet.java b/app/src/main/java/com/xabber/android/data/extension/avatar/BaseAvatarSet.java index 67cd5f804c..29440acefc 100644 --- a/app/src/main/java/com/xabber/android/data/extension/avatar/BaseAvatarSet.java +++ b/app/src/main/java/com/xabber/android/data/extension/avatar/BaseAvatarSet.java @@ -1,98 +1,125 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data.extension.avatar; -import java.util.HashMap; -import java.util.Map; - import android.content.res.TypedArray; import com.xabber.android.data.Application; import com.xabber.android.data.OnLowMemoryListener; +import java.util.HashMap; +import java.util.Map; + /** * Set of default avatars. - * + *

* Calculation takes not much time, so we don't use locks. - * + * * @author alexander.ivanov - * */ public class BaseAvatarSet implements OnLowMemoryListener { - /** - * Default resources. - */ - private final int[] AVATARS; - - /** - * Map with resource ids for specified uses. - */ - private final Map resources; - - public BaseAvatarSet(Application application, int array, int defaultDrawable) { - TypedArray defaultAvatars = application.getResources() - .obtainTypedArray(array); - AVATARS = new int[defaultAvatars.length()]; - for (int index = 0; index < defaultAvatars.length(); index++) - AVATARS[index] = defaultAvatars.getResourceId(index, - defaultDrawable); - defaultAvatars.recycle(); - resources = new HashMap(); - } - - /** - * Calculate avatar index for specified user. - * - * @param user - * @return - */ - protected int getIndex(String user) { - return user.hashCode(); - } - - /** - * Get drawable with default avatar of user. - * - * @param user - * @return - */ - public int getResourceId(String user) { - Integer resource = resources.get(user); - if (resource == null) { - resource = getElement(getIndex(user), AVATARS); - resources.put(user, resource); - } - return resource; - } - - /** - * Gets element from array by index. - * - * @param index - * @param array - * @return Always return element even if array's length is less then index. - */ - private int getElement(int index, int[] array) { - index = index % array.length; - if (index < 0) - index += array.length; - return array[index]; - } - - @Override - public void onLowMemory() { - resources.clear(); - } + /** + * Default resources. + */ + private final int[] avatarIconsResources; + + private final int[] colors; + + /** + * Map with resource ids for specified uses. + */ + private final Map resources; + + public static class DefaultAvatar { + public DefaultAvatar(int iconResource, int backgroundColor) { + this.iconResource = iconResource; + this.backgroundColor = backgroundColor; + } + + public int getIconResource() { + return iconResource; + } + + public int getBackgroundColor() { + return backgroundColor; + } + + private int iconResource; + private int backgroundColor; + } + + + public BaseAvatarSet(Application application, int avatarIconsArrayId, int avatarColorsArrayId) { + TypedArray defaultAvatars = application.getResources().obtainTypedArray(avatarIconsArrayId); + avatarIconsResources = new int[defaultAvatars.length()]; + for (int index = 0; index < defaultAvatars.length(); index++) { + avatarIconsResources[index] = defaultAvatars.getResourceId(index, -1); + } + defaultAvatars.recycle(); + + colors = application.getResources().getIntArray(avatarColorsArrayId); + + resources = new HashMap<>(); + } + + /** + * Calculate avatar index for specified user. + * + * @param user + * @return + */ + protected int getIndex(String user) { + return user.hashCode(); + } + + /** + * Get drawable with default avatar of user. + * + * @param user + * @return + */ + public DefaultAvatar getResourceId(String user) { + DefaultAvatar avatar = resources.get(user); + if (avatar == null) { + avatar = getElement(getIndex(user)); + resources.put(user, avatar); + } + return avatar; + } + + /** + * Gets element from array by index. + * + * @param index + * @param array + * @return Always return element even if array's length is less then index. + */ + private DefaultAvatar getElement(int index) { + int uniqueCombinationsNumber = avatarIconsResources.length * colors.length; + + index = index % uniqueCombinationsNumber; + + if (index < 0) { + index += uniqueCombinationsNumber; + } + + return new DefaultAvatar(avatarIconsResources[index / colors.length], colors[index % colors.length]); + } + + @Override + public void onLowMemory() { + resources.clear(); + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/avatar/BaseShortcutHelper.java b/app/src/main/java/com/xabber/android/data/extension/avatar/BaseShortcutHelper.java index 6937000159..fb67f8f645 100644 --- a/app/src/main/java/com/xabber/android/data/extension/avatar/BaseShortcutHelper.java +++ b/app/src/main/java/com/xabber/android/data/extension/avatar/BaseShortcutHelper.java @@ -6,24 +6,23 @@ /** * Helper class to create shortcuts under Android < 2.3. - * + * * @author alexander.ivanov - * */ public class BaseShortcutHelper { - /** - * Get the preferred launcher icon size. This is used when custom drawables - * are created (e.g., for shortcuts). - * - * Based on {@link android.app.ActivityManager#getLauncherLargeIconSize()} - * for Android 3+. - * - * @return dimensions of square icons in terms of pixels - */ - static int getLauncherLargeIconSize() { - final Resources res = Application.getInstance().getResources(); - return res.getDimensionPixelSize(android.R.dimen.app_icon_size); - } + /** + * Get the preferred launcher icon size. This is used when custom drawables + * are created (e.g., for shortcuts). + *

+ * Based on {@link android.app.ActivityManager#getLauncherLargeIconSize()} + * for Android 3+. + * + * @return dimensions of square icons in terms of pixels + */ + static int getLauncherLargeIconSize() { + final Resources res = Application.getInstance().getResources(); + return res.getDimensionPixelSize(android.R.dimen.app_icon_size); + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/avatar/GingerbreadShortcutHelper.java b/app/src/main/java/com/xabber/android/data/extension/avatar/GingerbreadShortcutHelper.java index 31aba71e70..4f31325927 100644 --- a/app/src/main/java/com/xabber/android/data/extension/avatar/GingerbreadShortcutHelper.java +++ b/app/src/main/java/com/xabber/android/data/extension/avatar/GingerbreadShortcutHelper.java @@ -10,47 +10,46 @@ /** * Helper class to create shortcuts under Android >= 2.3. - * + * * @author alexander.ivanov - * */ @TargetApi(Build.VERSION_CODES.GINGERBREAD) public class GingerbreadShortcutHelper { - /** - * Get the preferred launcher icon size. This is used when custom drawables - * are created (e.g., for shortcuts). - * - * Based on {@link android.app.ActivityManager#getLauncherLargeIconSize()} - * for Android 3+. - * - * @return dimensions of square icons in terms of pixels - */ - static int getLauncherLargeIconSize() { - final Resources res = Application.getInstance().getResources(); - final int size = res - .getDimensionPixelSize(android.R.dimen.app_icon_size); - if ((res.getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) != Configuration.SCREENLAYOUT_SIZE_XLARGE) { - return size; - } - final int density = res.getDisplayMetrics().densityDpi; + /** + * Get the preferred launcher icon size. This is used when custom drawables + * are created (e.g., for shortcuts). + *

+ * Based on {@link android.app.ActivityManager#getLauncherLargeIconSize()} + * for Android 3+. + * + * @return dimensions of square icons in terms of pixels + */ + static int getLauncherLargeIconSize() { + final Resources res = Application.getInstance().getResources(); + final int size = res + .getDimensionPixelSize(android.R.dimen.app_icon_size); + if ((res.getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) != Configuration.SCREENLAYOUT_SIZE_XLARGE) { + return size; + } + final int density = res.getDisplayMetrics().densityDpi; - switch (density) { - case DisplayMetrics.DENSITY_LOW: - return (size * DisplayMetrics.DENSITY_MEDIUM) - / DisplayMetrics.DENSITY_LOW; - case DisplayMetrics.DENSITY_MEDIUM: - return (size * DisplayMetrics.DENSITY_HIGH) - / DisplayMetrics.DENSITY_MEDIUM; - case DisplayMetrics.DENSITY_HIGH: - return (size * DisplayMetrics.DENSITY_XHIGH) - / DisplayMetrics.DENSITY_HIGH; - case DisplayMetrics.DENSITY_XHIGH: - return (size * DisplayMetrics.DENSITY_MEDIUM * 2) - / DisplayMetrics.DENSITY_XHIGH; - default: - return size; - } - } + switch (density) { + case DisplayMetrics.DENSITY_LOW: + return (size * DisplayMetrics.DENSITY_MEDIUM) + / DisplayMetrics.DENSITY_LOW; + case DisplayMetrics.DENSITY_MEDIUM: + return (size * DisplayMetrics.DENSITY_HIGH) + / DisplayMetrics.DENSITY_MEDIUM; + case DisplayMetrics.DENSITY_HIGH: + return (size * DisplayMetrics.DENSITY_XHIGH) + / DisplayMetrics.DENSITY_HIGH; + case DisplayMetrics.DENSITY_XHIGH: + return (size * DisplayMetrics.DENSITY_MEDIUM * 2) + / DisplayMetrics.DENSITY_XHIGH; + default: + return size; + } + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/avatar/HoneycombShortcutHelper.java b/app/src/main/java/com/xabber/android/data/extension/avatar/HoneycombShortcutHelper.java index 24dbe686dd..563e505dc2 100644 --- a/app/src/main/java/com/xabber/android/data/extension/avatar/HoneycombShortcutHelper.java +++ b/app/src/main/java/com/xabber/android/data/extension/avatar/HoneycombShortcutHelper.java @@ -9,39 +9,38 @@ /** * Helper class to create shortcuts under Android >= 3. - * + * * @author alexander.ivanov - * */ public class HoneycombShortcutHelper { - /** - * Get the preferred launcher icon size. This is used when custom drawables - * are created (e.g., for shortcuts). - * - * @return dimensions of square icons in terms of pixels - */ - static int getLauncherLargeIconSize() { - android.app.ActivityManager activityManager = (android.app.ActivityManager) Application - .getInstance().getSystemService(Context.ACTIVITY_SERVICE); - Method method; - try { - method = activityManager.getClass().getMethod( - "getLauncherLargeIconSize", (Class[]) null); - } catch (SecurityException e) { - throw new RuntimeException(e); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } - try { - return (Integer) method.invoke(activityManager, (Object[]) null); - } catch (IllegalArgumentException e) { - throw new RuntimeException(e); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } catch (InvocationTargetException e) { - throw new RuntimeException(e); - } - } + /** + * Get the preferred launcher icon size. This is used when custom drawables + * are created (e.g., for shortcuts). + * + * @return dimensions of square icons in terms of pixels + */ + static int getLauncherLargeIconSize() { + android.app.ActivityManager activityManager = (android.app.ActivityManager) Application + .getInstance().getSystemService(Context.ACTIVITY_SERVICE); + Method method; + try { + method = activityManager.getClass().getMethod( + "getLauncherLargeIconSize", (Class[]) null); + } catch (SecurityException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + try { + return (Integer) method.invoke(activityManager, (Object[]) null); + } catch (IllegalArgumentException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/capability/CapabilitiesManager.java b/app/src/main/java/com/xabber/android/data/extension/capability/CapabilitiesManager.java index cb671e6953..2dea7dec89 100644 --- a/app/src/main/java/com/xabber/android/data/extension/capability/CapabilitiesManager.java +++ b/app/src/main/java/com/xabber/android/data/extension/capability/CapabilitiesManager.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -59,466 +59,464 @@ /** * Provide information about entity capabilities. - * + * * @author alexander.ivanov - * */ public class CapabilitiesManager implements OnAuthorizedListener, - OnDisconnectListener, OnAccountRemovedListener, OnPacketListener, - OnLoadListener { - - private static final String FORM_TYPE = "FORM_TYPE"; - - public static final ClientInfo INVALID_CLIENT_INFO = new ClientInfo(null, - null, null, new ArrayList()); - - private final static CapabilitiesManager instance; - - static { - instance = new CapabilitiesManager(); - Application.getInstance().addManager(instance); - } - - public static CapabilitiesManager getInstance() { - return instance; - } - - /** - * List of sent requests. - */ - private final Collection requests; - - /** - * Capability information for full jid in account. - */ - private final NestedMap userCapabilities; - - /** - * Capabilities information with associated discovery information. - */ - private final Map clientInformations; - - private CapabilitiesManager() { - requests = new ArrayList(); - userCapabilities = new NestedMap(); - clientInformations = new HashMap(); - } - - @Override - public void onLoad() { - Cursor cursor = CapabilitiesTable.getInstance().list(); - final Map clientInformations = new HashMap(); - try { - if (cursor.moveToFirst()) { - do { - clientInformations.put(new Capability(null, null, - CapabilitiesTable.getHash(cursor), - CapabilitiesTable.getNode(cursor), - CapabilitiesTable.getVersion(cursor)), - new ClientInfo(CapabilitiesTable.getType(cursor), - CapabilitiesTable.getName(cursor), - CapabilitiesTable.getNode(cursor), - CapabilitiesTable.getFeatures(cursor))); - } while (cursor.moveToNext()); - } - } finally { - cursor.close(); - } - Application.getInstance().runOnUiThread(new Runnable() { - @Override - public void run() { - onLoaded(clientInformations); - } - }); - } - - private void onLoaded(Map clientInformations) { - this.clientInformations.putAll(clientInformations); - } - - /** - * Returns information about client. - * - * @param account - * @param user - * full JID. - * @return null if there is no available information. - */ - public ClientInfo getClientInfo(String account, String user) { - Capability capability = userCapabilities.get(account, - Jid.getStringPrep(user)); - if (capability == null) - return null; - return clientInformations.get(capability); - } - - /** - * @param discoverInfo - * @return Client information. - */ - private Collection getFeatures(DiscoverInfo discoverInfo) { - Collection features = new ArrayList(); - for (Iterator iterator = discoverInfo.getFeatures(); iterator - .hasNext();) - features.add(iterator.next().getVar()); - return features; - } - - /** - * @param discoverInfo - * @return Client information. - */ - private ClientInfo getClientInfo(DiscoverInfo discoverInfo) { - for (int useClient = 1; useClient >= 0; useClient--) - for (int useLanguage = 2; useLanguage >= 0; useLanguage--) - for (Iterator iterator = discoverInfo.getIdentities(); iterator - .hasNext();) { - Identity identity = iterator.next(); - if (useClient == 1 - && !"client".equals(identity.getCategory())) - continue; - if (useLanguage == 2 - && !Packet.getDefaultLanguage().equals( - identity.getLanguage())) - continue; - if (useLanguage == 1 && identity.getLanguage() != null) - continue; - return new ClientInfo(identity.getType(), - identity.getName(), discoverInfo.getNode(), - getFeatures(discoverInfo)); - } - return new ClientInfo(null, null, null, getFeatures(discoverInfo)); - } - - /** - * Requests disco info. - * - * @param account - * @param user - */ - public void request(String account, String user) { - user = Jid.getStringPrep(user); - Capability capability = new Capability(account, - Jid.getStringPrep(user), Capability.DIRECT_REQUEST_METHOD, - null, null); - userCapabilities.put(account, Jid.getStringPrep(user), capability); - request(account, user, capability); - } - - /** - * Requests disco info. - * - * @param account - * @param user - * @param capability - */ - private void request(String account, String user, Capability capability) { - for (DiscoverInfoRequest check : requests) - if (capability.equals(check.getCapability())) - return; - DiscoverInfo packet = new DiscoverInfo(); - packet.setTo(user); - packet.setType(Type.GET); - if (capability.getNode() != null && capability.getVersion() != null) - packet.setNode(capability.getNode() + "#" + capability.getVersion()); - try { - ConnectionManager.getInstance().sendPacket(account, packet); - } catch (NetworkException e) { - return; - } - requests.add(new DiscoverInfoRequest(account, Jid.getStringPrep(user), - packet.getPacketID(), capability)); - } - - private boolean isValid(DiscoverInfo discoverInfo) { - Set identities = new TreeSet( - new Comparator() { - - private int compare(String string1, String string2) { - return (string1 == null ? "" : string1) - .compareTo(string2 == null ? "" : string2); - } - - @Override - public int compare(Identity identity1, Identity identity2) { - int result; - result = compare(identity1.getCategory(), - identity2.getCategory()); - if (result != 0) - return result; - result = compare(identity1.getType(), - identity2.getType()); - if (result != 0) - return result; - result = compare(identity1.getLanguage(), - identity2.getLanguage()); - if (result != 0) - return result; - result = compare(identity1.getName(), - identity2.getName()); - if (result != 0) - return result; - return 0; - } - - }); - for (Iterator iterator = discoverInfo.getIdentities(); iterator - .hasNext();) - if (!identities.add(iterator.next())) - return false; - Set features = new HashSet(); - for (Iterator iterator = discoverInfo.getFeatures(); iterator - .hasNext();) - if (!features.add(iterator.next().getVar())) - return false; - Set formTypes = new HashSet(); - for (PacketExtension packetExtension : discoverInfo.getExtensions()) - if (packetExtension instanceof DataForm) { - DataForm dataForm = (DataForm) packetExtension; - String formType = null; - for (Iterator iterator = dataForm.getFields(); iterator - .hasNext();) { - FormField formField = iterator.next(); - if (FORM_TYPE.equals(formField.getVariable())) { - for (Iterator iterator2 = formField.getValues(); iterator2 - .hasNext();) { - String value = iterator2.next(); - if (formType != null && !formType.equals(value)) - return false; - formType = value; - } - } - } - if (!formTypes.add(formType)) - return false; - } - return true; - } - - private String calculateString(DiscoverInfo discoverInfo) { - StringBuilder s = new StringBuilder(); - - SortedSet identities = new TreeSet(); - for (Iterator iterator = discoverInfo.getIdentities(); iterator - .hasNext();) { - Identity identity = iterator.next(); - StringBuilder builder = new StringBuilder(); - builder.append(identity.getCategory()); - builder.append("/"); - String type = identity.getType(); - if (type != null) - builder.append(type); - builder.append("/"); - String lang = identity.getLanguage(); - if (lang != null) - builder.append(lang); - builder.append("/"); - String name = identity.getName(); - if (name != null) - builder.append(name); - identities.add(builder.toString()); - } - for (String identity : identities) { - s.append(identity); - s.append("<"); - } - - SortedSet features = new TreeSet(); - for (Iterator iterator = discoverInfo.getFeatures(); iterator - .hasNext();) - features.add(iterator.next().getVar()); - for (String feature : features) { - s.append(feature); - s.append("<"); - } - - // Maps prepared value to FORM_TYPE key. - // Extensions with equal FORM_TYPEs are not allowed. - SortedMap extendeds = new TreeMap(); - for (PacketExtension packetExtension : discoverInfo.getExtensions()) - if (packetExtension instanceof DataForm) { - DataForm dataForm = (DataForm) packetExtension; - // Fields with equal var are allowed for fixed type. - SortedSet formFields = new TreeSet( - new Comparator() { - @Override - public int compare(FormField f1, FormField f2) { - // Var may not exists for fixed type. - String s1 = f1.getVariable(); - String s2 = f2.getVariable(); - return (s1 == null ? "" : s1) - .compareTo(s2 == null ? "" : s2); - } - }); - String formType = null; - for (Iterator iterator = dataForm.getFields(); iterator - .hasNext();) { - FormField formField = iterator.next(); - if (FORM_TYPE.equals(formField.getVariable())) { - if (!FormField.TYPE_HIDDEN.equals(formField.getType())) - continue; - for (Iterator iterator2 = formField.getValues(); iterator2 - .hasNext();) - formType = iterator2.next(); - } else { - formFields.add(formField); - } - } - if (formType == null) - continue; - StringBuilder builder = new StringBuilder(); - builder.append(formType); - builder.append("<"); - for (FormField formField : formFields) { - builder.append(formField.getVariable()); - builder.append("<"); - SortedSet values = new TreeSet(); - for (Iterator iterator2 = formField.getValues(); iterator2 - .hasNext();) - values.add(iterator2.next()); - for (String value : values) { - builder.append(value); - builder.append("<"); - } - } - extendeds.put(formType, builder.toString()); - } - for (Entry extended : extendeds.entrySet()) - s.append(extended.getValue()); - return s.toString(); - } - - @Override - public void onAuthorized(ConnectionItem connection) { - if (!(connection instanceof AccountItem)) - return; - removeAccountInfo(((AccountItem) connection).getAccount()); - } - - @Override - public void onAccountRemoved(AccountItem accountItem) { - removeAccountInfo(accountItem.getAccount()); - } - - private void removeAccountInfo(String account) { - userCapabilities.clear(account); - Iterator iterator = clientInformations.keySet().iterator(); - while (iterator.hasNext()) - if (account.equals(iterator.next().getAccount())) - iterator.remove(); - } - - @Override - public void onDisconnect(ConnectionItem connection) { - if (!(connection instanceof AccountItem)) - return; - String account = ((AccountItem) connection).getAccount(); - Iterator iterator = requests.iterator(); - while (iterator.hasNext()) { - if (iterator.next().getAccount().equals(account)) - iterator.remove(); - } - } - - @Override - public void onPacket(ConnectionItem connection, String bareAddress, - Packet packet) { - if (!(connection instanceof AccountItem)) - return; - final String account = ((AccountItem) connection).getAccount(); - final String user = Jid.getStringPrep(packet.getFrom()); - if (packet instanceof Presence) { - if (user == null) - return; - final Presence presence = (Presence) packet; - if (presence.getType() == Presence.Type.error) - return; - if (presence.getType() == Presence.Type.unavailable) { - userCapabilities.remove(account, user); - return; - } - for (PacketExtension packetExtension : presence.getExtensions()) - if (packetExtension instanceof CapsExtension) { - CapsExtension capsExtension = (CapsExtension) packetExtension; - if (capsExtension.getNode() == null - || capsExtension.getVersion() == null) - continue; - Capability capability = new Capability(account, user, - capsExtension.getHash(), capsExtension.getNode(), - capsExtension.getVersion()); - if (capability.equals(userCapabilities.get(account, user))) - continue; - userCapabilities.put(account, user, capability); - ClientInfo clientInfo = clientInformations.get(capability); - if (clientInfo == null || clientInfo == INVALID_CLIENT_INFO) - request(account, packet.getFrom(), capability); - } - } else if (packet instanceof IQ) { - IQ iq = (IQ) packet; - if (iq.getType() != Type.ERROR - && !(packet instanceof DiscoverInfo && iq.getType() == Type.RESULT)) - return; - String packetId = iq.getPacketID(); - DiscoverInfoRequest request = null; - Iterator iterator = requests.iterator(); - while (iterator.hasNext()) { - DiscoverInfoRequest check = iterator.next(); - if (check.getPacketId().equals(packetId)) { - request = check; - iterator.remove(); - break; - } - } - if (request == null || !request.getUser().equals(user)) - return; - final Capability capability = request.getCapability(); - final ClientInfo clientInfo; - if (iq.getType() == Type.ERROR) { - if (!Capability.DIRECT_REQUEST_METHOD.equals(capability - .getHash())) - // Don't save invalid replay if it wasn't direct request. - return; - if (clientInformations.containsKey(capability)) - return; - clientInfo = INVALID_CLIENT_INFO; - } else if (iq.getType() == Type.RESULT) { - DiscoverInfo discoverInfo = (DiscoverInfo) packet; - if (capability.isSupportedHash() || capability.isLegacy()) { - if (capability.isLegacy() - || (isValid(discoverInfo) && capability - .getHashedValue( - calculateString(discoverInfo)) - .equals(capability.getVersion()))) { - clientInfo = getClientInfo(discoverInfo); - Application.getInstance().runInBackground( - new Runnable() { - @Override - public void run() { - CapabilitiesTable.getInstance().write( - capability.getHash(), - capability.getNode(), - capability.getVersion(), - clientInfo.getType(), - clientInfo.getName(), - clientInfo.getFeatures()); - } - }); - } else { - // Just wait for next presence from another entity. - return; - } - } else { - clientInfo = getClientInfo(discoverInfo); - } - } else - throw new IllegalStateException(); - clientInformations.put(capability, clientInfo); - ArrayList entities = new ArrayList(); - for (NestedMap.Entry entry : userCapabilities) - if (capability.equals(entry.getValue())) - entities.add(new BaseEntity(account, Jid - .getBareAddress(entry.getSecond()))); - RosterManager.getInstance().onContactsChanged(entities); - } - } + OnDisconnectListener, OnAccountRemovedListener, OnPacketListener, + OnLoadListener { + + private static final String FORM_TYPE = "FORM_TYPE"; + + public static final ClientInfo INVALID_CLIENT_INFO = new ClientInfo(null, + null, null, new ArrayList()); + + private final static CapabilitiesManager instance; + + static { + instance = new CapabilitiesManager(); + Application.getInstance().addManager(instance); + } + + public static CapabilitiesManager getInstance() { + return instance; + } + + /** + * List of sent requests. + */ + private final Collection requests; + + /** + * Capability information for full jid in account. + */ + private final NestedMap userCapabilities; + + /** + * Capabilities information with associated discovery information. + */ + private final Map clientInformations; + + private CapabilitiesManager() { + requests = new ArrayList(); + userCapabilities = new NestedMap(); + clientInformations = new HashMap(); + } + + @Override + public void onLoad() { + Cursor cursor = CapabilitiesTable.getInstance().list(); + final Map clientInformations = new HashMap(); + try { + if (cursor.moveToFirst()) { + do { + clientInformations.put(new Capability(null, null, + CapabilitiesTable.getHash(cursor), + CapabilitiesTable.getNode(cursor), + CapabilitiesTable.getVersion(cursor)), + new ClientInfo(CapabilitiesTable.getType(cursor), + CapabilitiesTable.getName(cursor), + CapabilitiesTable.getNode(cursor), + CapabilitiesTable.getFeatures(cursor))); + } while (cursor.moveToNext()); + } + } finally { + cursor.close(); + } + Application.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + onLoaded(clientInformations); + } + }); + } + + private void onLoaded(Map clientInformations) { + this.clientInformations.putAll(clientInformations); + } + + /** + * Returns information about client. + * + * @param account + * @param user full JID. + * @return null if there is no available information. + */ + public ClientInfo getClientInfo(String account, String user) { + Capability capability = userCapabilities.get(account, + Jid.getStringPrep(user)); + if (capability == null) + return null; + return clientInformations.get(capability); + } + + /** + * @param discoverInfo + * @return Client information. + */ + private Collection getFeatures(DiscoverInfo discoverInfo) { + Collection features = new ArrayList(); + for (Iterator iterator = discoverInfo.getFeatures(); iterator + .hasNext(); ) + features.add(iterator.next().getVar()); + return features; + } + + /** + * @param discoverInfo + * @return Client information. + */ + private ClientInfo getClientInfo(DiscoverInfo discoverInfo) { + for (int useClient = 1; useClient >= 0; useClient--) + for (int useLanguage = 2; useLanguage >= 0; useLanguage--) + for (Iterator iterator = discoverInfo.getIdentities(); iterator + .hasNext(); ) { + Identity identity = iterator.next(); + if (useClient == 1 + && !"client".equals(identity.getCategory())) + continue; + if (useLanguage == 2 + && !Packet.getDefaultLanguage().equals( + identity.getLanguage())) + continue; + if (useLanguage == 1 && identity.getLanguage() != null) + continue; + return new ClientInfo(identity.getType(), + identity.getName(), discoverInfo.getNode(), + getFeatures(discoverInfo)); + } + return new ClientInfo(null, null, null, getFeatures(discoverInfo)); + } + + /** + * Requests disco info. + * + * @param account + * @param user + */ + public void request(String account, String user) { + user = Jid.getStringPrep(user); + Capability capability = new Capability(account, + Jid.getStringPrep(user), Capability.DIRECT_REQUEST_METHOD, + null, null); + userCapabilities.put(account, Jid.getStringPrep(user), capability); + request(account, user, capability); + } + + /** + * Requests disco info. + * + * @param account + * @param user + * @param capability + */ + private void request(String account, String user, Capability capability) { + for (DiscoverInfoRequest check : requests) + if (capability.equals(check.getCapability())) + return; + DiscoverInfo packet = new DiscoverInfo(); + packet.setTo(user); + packet.setType(Type.GET); + if (capability.getNode() != null && capability.getVersion() != null) + packet.setNode(capability.getNode() + "#" + capability.getVersion()); + try { + ConnectionManager.getInstance().sendPacket(account, packet); + } catch (NetworkException e) { + return; + } + requests.add(new DiscoverInfoRequest(account, Jid.getStringPrep(user), + packet.getPacketID(), capability)); + } + + private boolean isValid(DiscoverInfo discoverInfo) { + Set identities = new TreeSet( + new Comparator() { + + private int compare(String string1, String string2) { + return (string1 == null ? "" : string1) + .compareTo(string2 == null ? "" : string2); + } + + @Override + public int compare(Identity identity1, Identity identity2) { + int result; + result = compare(identity1.getCategory(), + identity2.getCategory()); + if (result != 0) + return result; + result = compare(identity1.getType(), + identity2.getType()); + if (result != 0) + return result; + result = compare(identity1.getLanguage(), + identity2.getLanguage()); + if (result != 0) + return result; + result = compare(identity1.getName(), + identity2.getName()); + if (result != 0) + return result; + return 0; + } + + }); + for (Iterator iterator = discoverInfo.getIdentities(); iterator + .hasNext(); ) + if (!identities.add(iterator.next())) + return false; + Set features = new HashSet(); + for (Iterator iterator = discoverInfo.getFeatures(); iterator + .hasNext(); ) + if (!features.add(iterator.next().getVar())) + return false; + Set formTypes = new HashSet(); + for (PacketExtension packetExtension : discoverInfo.getExtensions()) + if (packetExtension instanceof DataForm) { + DataForm dataForm = (DataForm) packetExtension; + String formType = null; + for (Iterator iterator = dataForm.getFields(); iterator + .hasNext(); ) { + FormField formField = iterator.next(); + if (FORM_TYPE.equals(formField.getVariable())) { + for (Iterator iterator2 = formField.getValues(); iterator2 + .hasNext(); ) { + String value = iterator2.next(); + if (formType != null && !formType.equals(value)) + return false; + formType = value; + } + } + } + if (!formTypes.add(formType)) + return false; + } + return true; + } + + private String calculateString(DiscoverInfo discoverInfo) { + StringBuilder s = new StringBuilder(); + + SortedSet identities = new TreeSet(); + for (Iterator iterator = discoverInfo.getIdentities(); iterator + .hasNext(); ) { + Identity identity = iterator.next(); + StringBuilder builder = new StringBuilder(); + builder.append(identity.getCategory()); + builder.append("/"); + String type = identity.getType(); + if (type != null) + builder.append(type); + builder.append("/"); + String lang = identity.getLanguage(); + if (lang != null) + builder.append(lang); + builder.append("/"); + String name = identity.getName(); + if (name != null) + builder.append(name); + identities.add(builder.toString()); + } + for (String identity : identities) { + s.append(identity); + s.append("<"); + } + + SortedSet features = new TreeSet(); + for (Iterator iterator = discoverInfo.getFeatures(); iterator + .hasNext(); ) + features.add(iterator.next().getVar()); + for (String feature : features) { + s.append(feature); + s.append("<"); + } + + // Maps prepared value to FORM_TYPE key. + // Extensions with equal FORM_TYPEs are not allowed. + SortedMap extendeds = new TreeMap(); + for (PacketExtension packetExtension : discoverInfo.getExtensions()) + if (packetExtension instanceof DataForm) { + DataForm dataForm = (DataForm) packetExtension; + // Fields with equal var are allowed for fixed type. + SortedSet formFields = new TreeSet( + new Comparator() { + @Override + public int compare(FormField f1, FormField f2) { + // Var may not exists for fixed type. + String s1 = f1.getVariable(); + String s2 = f2.getVariable(); + return (s1 == null ? "" : s1) + .compareTo(s2 == null ? "" : s2); + } + }); + String formType = null; + for (Iterator iterator = dataForm.getFields(); iterator + .hasNext(); ) { + FormField formField = iterator.next(); + if (FORM_TYPE.equals(formField.getVariable())) { + if (!FormField.TYPE_HIDDEN.equals(formField.getType())) + continue; + for (Iterator iterator2 = formField.getValues(); iterator2 + .hasNext(); ) + formType = iterator2.next(); + } else { + formFields.add(formField); + } + } + if (formType == null) + continue; + StringBuilder builder = new StringBuilder(); + builder.append(formType); + builder.append("<"); + for (FormField formField : formFields) { + builder.append(formField.getVariable()); + builder.append("<"); + SortedSet values = new TreeSet(); + for (Iterator iterator2 = formField.getValues(); iterator2 + .hasNext(); ) + values.add(iterator2.next()); + for (String value : values) { + builder.append(value); + builder.append("<"); + } + } + extendeds.put(formType, builder.toString()); + } + for (Entry extended : extendeds.entrySet()) + s.append(extended.getValue()); + return s.toString(); + } + + @Override + public void onAuthorized(ConnectionItem connection) { + if (!(connection instanceof AccountItem)) + return; + removeAccountInfo(((AccountItem) connection).getAccount()); + } + + @Override + public void onAccountRemoved(AccountItem accountItem) { + removeAccountInfo(accountItem.getAccount()); + } + + private void removeAccountInfo(String account) { + userCapabilities.clear(account); + Iterator iterator = clientInformations.keySet().iterator(); + while (iterator.hasNext()) + if (account.equals(iterator.next().getAccount())) + iterator.remove(); + } + + @Override + public void onDisconnect(ConnectionItem connection) { + if (!(connection instanceof AccountItem)) + return; + String account = ((AccountItem) connection).getAccount(); + Iterator iterator = requests.iterator(); + while (iterator.hasNext()) { + if (iterator.next().getAccount().equals(account)) + iterator.remove(); + } + } + + @Override + public void onPacket(ConnectionItem connection, String bareAddress, + Packet packet) { + if (!(connection instanceof AccountItem)) + return; + final String account = ((AccountItem) connection).getAccount(); + final String user = Jid.getStringPrep(packet.getFrom()); + if (packet instanceof Presence) { + if (user == null) + return; + final Presence presence = (Presence) packet; + if (presence.getType() == Presence.Type.error) + return; + if (presence.getType() == Presence.Type.unavailable) { + userCapabilities.remove(account, user); + return; + } + for (PacketExtension packetExtension : presence.getExtensions()) + if (packetExtension instanceof CapsExtension) { + CapsExtension capsExtension = (CapsExtension) packetExtension; + if (capsExtension.getNode() == null + || capsExtension.getVersion() == null) + continue; + Capability capability = new Capability(account, user, + capsExtension.getHash(), capsExtension.getNode(), + capsExtension.getVersion()); + if (capability.equals(userCapabilities.get(account, user))) + continue; + userCapabilities.put(account, user, capability); + ClientInfo clientInfo = clientInformations.get(capability); + if (clientInfo == null || clientInfo == INVALID_CLIENT_INFO) + request(account, packet.getFrom(), capability); + } + } else if (packet instanceof IQ) { + IQ iq = (IQ) packet; + if (iq.getType() != Type.ERROR + && !(packet instanceof DiscoverInfo && iq.getType() == Type.RESULT)) + return; + String packetId = iq.getPacketID(); + DiscoverInfoRequest request = null; + Iterator iterator = requests.iterator(); + while (iterator.hasNext()) { + DiscoverInfoRequest check = iterator.next(); + if (check.getPacketId().equals(packetId)) { + request = check; + iterator.remove(); + break; + } + } + if (request == null || !request.getUser().equals(user)) + return; + final Capability capability = request.getCapability(); + final ClientInfo clientInfo; + if (iq.getType() == Type.ERROR) { + if (!Capability.DIRECT_REQUEST_METHOD.equals(capability + .getHash())) + // Don't save invalid replay if it wasn't direct request. + return; + if (clientInformations.containsKey(capability)) + return; + clientInfo = INVALID_CLIENT_INFO; + } else if (iq.getType() == Type.RESULT) { + DiscoverInfo discoverInfo = (DiscoverInfo) packet; + if (capability.isSupportedHash() || capability.isLegacy()) { + if (capability.isLegacy() + || (isValid(discoverInfo) && capability + .getHashedValue( + calculateString(discoverInfo)) + .equals(capability.getVersion()))) { + clientInfo = getClientInfo(discoverInfo); + Application.getInstance().runInBackground( + new Runnable() { + @Override + public void run() { + CapabilitiesTable.getInstance().write( + capability.getHash(), + capability.getNode(), + capability.getVersion(), + clientInfo.getType(), + clientInfo.getName(), + clientInfo.getFeatures()); + } + }); + } else { + // Just wait for next presence from another entity. + return; + } + } else { + clientInfo = getClientInfo(discoverInfo); + } + } else + throw new IllegalStateException(); + clientInformations.put(capability, clientInfo); + ArrayList entities = new ArrayList(); + for (NestedMap.Entry entry : userCapabilities) + if (capability.equals(entry.getValue())) + entities.add(new BaseEntity(account, Jid + .getBareAddress(entry.getSecond()))); + RosterManager.getInstance().onContactsChanged(entities); + } + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/capability/CapabilitiesTable.java b/app/src/main/java/com/xabber/android/data/extension/capability/CapabilitiesTable.java index ebc0e38f17..ee17c5dc14 100644 --- a/app/src/main/java/com/xabber/android/data/extension/capability/CapabilitiesTable.java +++ b/app/src/main/java/com/xabber/android/data/extension/capability/CapabilitiesTable.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -26,172 +26,172 @@ /** * Storage with hashed entity capabilities. - * + * * @author alexander.ivanov */ class CapabilitiesTable extends AbstractTable { - private static final class Fields implements BaseColumns { - private Fields() { - } - - /** - * Hash method. - */ - public static final String HASH = "hash"; - - /** - * Node name. - */ - public static final String NODE = "node"; - - /** - * Hashed capabilities. - */ - public static final String VERSION = "version"; - - /** - * Client type. - */ - public static final String TYPE = "type_"; - - /** - * Client name. - */ - public static final String NAME = "name"; - - /** - * Comma separated list of client features. - */ - public static final String FEATURES = "features"; - } - - private static final String NAME = "capabilities"; - private static final String[] PROJECTION = new String[] { Fields.HASH, - Fields.NODE, Fields.VERSION, Fields.TYPE, Fields.NAME, - Fields.FEATURES }; - - private final DatabaseManager databaseManager; - private SQLiteStatement writeStatement; - private final Object writeLock; - - private final static CapabilitiesTable instance; - - static { - instance = new CapabilitiesTable(DatabaseManager.getInstance()); - DatabaseManager.getInstance().addTable(instance); - } - - public static CapabilitiesTable getInstance() { - return instance; - } - - private CapabilitiesTable(DatabaseManager databaseManager) { - this.databaseManager = databaseManager; - writeStatement = null; - writeLock = new Object(); - } - - @Override - public void create(SQLiteDatabase db) { - String sql = "CREATE TABLE " + NAME + " (" + Fields.HASH + " TEXT," - + Fields.NODE + " TEXT," + Fields.VERSION + " TEXT," - + Fields.TYPE + " TEXT," + Fields.NAME + " TEXT," - + Fields.FEATURES + " TEXT);"; - DatabaseManager.execSQL(db, sql); - sql = "CREATE UNIQUE INDEX " + NAME + "_index ON " + NAME + " (" - + Fields.HASH + ", " + Fields.NODE + ", " + Fields.VERSION - + ");"; - DatabaseManager.execSQL(db, sql); - } - - @Override - public void migrate(SQLiteDatabase db, int toVersion) { - super.migrate(db, toVersion); - String sql; - switch (toVersion) { - case 51: - sql = "CREATE TABLE capabilities (" + "hash TEXT," + "node TEXT," - + "version TEXT," + "type_ TEXT," + "name TEXT);"; - DatabaseManager.execSQL(db, sql); - sql = "CREATE UNIQUE INDEX capabilities_index ON capabilities " - + "(hash, node, version);"; - DatabaseManager.execSQL(db, sql); - break; - case 62: - sql = "ALTER TABLE capabilities ADD COLUMN features TEXT;"; - DatabaseManager.execSQL(db, sql); - sql = "DELETE FROM capabilities;"; - DatabaseManager.execSQL(db, sql); - break; - default: - break; - } - } - - void write(String hash, String node, String version, String type, - String name, Collection features) { - synchronized (writeLock) { - if (writeStatement == null) { - SQLiteDatabase db = databaseManager.getWritableDatabase(); - writeStatement = db.compileStatement("INSERT OR REPLACE INTO " - + NAME + " (" + Fields.HASH + ", " + Fields.NODE + ", " - + Fields.VERSION + ", " + Fields.TYPE + ", " - + Fields.NAME + ", " + Fields.FEATURES - + ") VALUES (?, ?, ?, ?, ?, ?);"); - } - if (hash == null) - writeStatement.bindNull(1); - else - writeStatement.bindString(1, hash); - writeStatement.bindString(2, node); - writeStatement.bindString(3, version); - if (type == null) - writeStatement.bindNull(4); - else - writeStatement.bindString(4, type); - if (name == null) - writeStatement.bindNull(5); - else - writeStatement.bindString(5, name); - writeStatement.bindString(6, - DatabaseManager.commaSeparatedFromCollection(features)); - writeStatement.execute(); - } - } - - @Override - protected String getTableName() { - return NAME; - } - - @Override - protected String[] getProjection() { - return PROJECTION; - } - - static String getHash(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.HASH)); - } - - static String getNode(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.NODE)); - } - - static String getVersion(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.VERSION)); - } - - static String getType(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.TYPE)); - } - - static String getName(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.NAME)); - } - - static Collection getFeatures(Cursor cursor) { - return DatabaseManager.collectionFromCommaSeparated(cursor - .getString(cursor.getColumnIndex(Fields.FEATURES))); - } + private static final class Fields implements BaseColumns { + private Fields() { + } + + /** + * Hash method. + */ + public static final String HASH = "hash"; + + /** + * Node name. + */ + public static final String NODE = "node"; + + /** + * Hashed capabilities. + */ + public static final String VERSION = "version"; + + /** + * Client type. + */ + public static final String TYPE = "type_"; + + /** + * Client name. + */ + public static final String NAME = "name"; + + /** + * Comma separated list of client features. + */ + public static final String FEATURES = "features"; + } + + private static final String NAME = "capabilities"; + private static final String[] PROJECTION = new String[]{Fields.HASH, + Fields.NODE, Fields.VERSION, Fields.TYPE, Fields.NAME, + Fields.FEATURES}; + + private final DatabaseManager databaseManager; + private SQLiteStatement writeStatement; + private final Object writeLock; + + private final static CapabilitiesTable instance; + + static { + instance = new CapabilitiesTable(DatabaseManager.getInstance()); + DatabaseManager.getInstance().addTable(instance); + } + + public static CapabilitiesTable getInstance() { + return instance; + } + + private CapabilitiesTable(DatabaseManager databaseManager) { + this.databaseManager = databaseManager; + writeStatement = null; + writeLock = new Object(); + } + + @Override + public void create(SQLiteDatabase db) { + String sql = "CREATE TABLE " + NAME + " (" + Fields.HASH + " TEXT," + + Fields.NODE + " TEXT," + Fields.VERSION + " TEXT," + + Fields.TYPE + " TEXT," + Fields.NAME + " TEXT," + + Fields.FEATURES + " TEXT);"; + DatabaseManager.execSQL(db, sql); + sql = "CREATE UNIQUE INDEX " + NAME + "_index ON " + NAME + " (" + + Fields.HASH + ", " + Fields.NODE + ", " + Fields.VERSION + + ");"; + DatabaseManager.execSQL(db, sql); + } + + @Override + public void migrate(SQLiteDatabase db, int toVersion) { + super.migrate(db, toVersion); + String sql; + switch (toVersion) { + case 51: + sql = "CREATE TABLE capabilities (" + "hash TEXT," + "node TEXT," + + "version TEXT," + "type_ TEXT," + "name TEXT);"; + DatabaseManager.execSQL(db, sql); + sql = "CREATE UNIQUE INDEX capabilities_index ON capabilities " + + "(hash, node, version);"; + DatabaseManager.execSQL(db, sql); + break; + case 62: + sql = "ALTER TABLE capabilities ADD COLUMN features TEXT;"; + DatabaseManager.execSQL(db, sql); + sql = "DELETE FROM capabilities;"; + DatabaseManager.execSQL(db, sql); + break; + default: + break; + } + } + + void write(String hash, String node, String version, String type, + String name, Collection features) { + synchronized (writeLock) { + if (writeStatement == null) { + SQLiteDatabase db = databaseManager.getWritableDatabase(); + writeStatement = db.compileStatement("INSERT OR REPLACE INTO " + + NAME + " (" + Fields.HASH + ", " + Fields.NODE + ", " + + Fields.VERSION + ", " + Fields.TYPE + ", " + + Fields.NAME + ", " + Fields.FEATURES + + ") VALUES (?, ?, ?, ?, ?, ?);"); + } + if (hash == null) + writeStatement.bindNull(1); + else + writeStatement.bindString(1, hash); + writeStatement.bindString(2, node); + writeStatement.bindString(3, version); + if (type == null) + writeStatement.bindNull(4); + else + writeStatement.bindString(4, type); + if (name == null) + writeStatement.bindNull(5); + else + writeStatement.bindString(5, name); + writeStatement.bindString(6, + DatabaseManager.commaSeparatedFromCollection(features)); + writeStatement.execute(); + } + } + + @Override + protected String getTableName() { + return NAME; + } + + @Override + protected String[] getProjection() { + return PROJECTION; + } + + static String getHash(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.HASH)); + } + + static String getNode(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.NODE)); + } + + static String getVersion(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.VERSION)); + } + + static String getType(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.TYPE)); + } + + static String getName(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.NAME)); + } + + static Collection getFeatures(Cursor cursor) { + return DatabaseManager.collectionFromCommaSeparated(cursor + .getString(cursor.getColumnIndex(Fields.FEATURES))); + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/data/extension/capability/Capability.java b/app/src/main/java/com/xabber/android/data/extension/capability/Capability.java index 0329c09404..61108c75f0 100644 --- a/app/src/main/java/com/xabber/android/data/extension/capability/Capability.java +++ b/app/src/main/java/com/xabber/android/data/extension/capability/Capability.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -23,139 +23,139 @@ class Capability extends BaseEntity { - private static final String SHA1_METHOD = "sha-1"; - - public static final String DIRECT_REQUEST_METHOD = "com.xabber.android.data.extension.Capability.DIRECT_REQUEST"; - - /** - * Hash mehdod. - */ - private final String hash; - - /** - * Product code. - */ - private final String node; - - /** - * Hashed capabilities. - */ - private final String version; - - public Capability(String account, String user, String hash, String node, - String version) { - super((isLegacy(hash) || isSupportedHash(hash)) ? null : account, - (isLegacy(hash) || isSupportedHash(hash)) ? null : user); - this.hash = hash; - this.node = node; - this.version = version; - } - - /** - * @return Hash method. - */ - public String getHash() { - return hash; - } - - /** - * @return Product code. - */ - public String getNode() { - return node; - } - - /** - * @return Hashed capabilities. - */ - public String getVersion() { - return version; - } - - /** - * @param value - * @return hasded value using hash method. - */ - public String getHashedValue(String value) { - try { - MessageDigest md = MessageDigest.getInstance(hash.toUpperCase()); - byte[] digest = md.digest(value.getBytes()); - return Base64.encodeBytes(digest); - } catch (NoSuchAlgorithmException nsae) { - return null; - } - } - - /** - * @param hash - * @return Whether hash method is supported. - */ - private static boolean isSupportedHash(String hash) { - return SHA1_METHOD.equals(hash); - } - - /** - * @return Whether hash method is supported. - */ - public boolean isSupportedHash() { - return isSupportedHash(hash); - } - - /** - * @param hash - * @return Whether this is legacy capability. - */ - private static boolean isLegacy(String hash) { - return hash == null; - } - - /** - * @return Whether this is legacy capability. - */ - public boolean isLegacy() { - return isLegacy(hash); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((hash == null) ? 0 : hash.hashCode()); - result = prime * result + ((node == null) ? 0 : node.hashCode()); - result = prime * result + ((version == null) ? 0 : version.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (!super.equals(obj)) - return false; - if (getClass() != obj.getClass()) - return false; - Capability other = (Capability) obj; - if (hash == null) { - if (other.hash != null) - return false; - } else if (!hash.equals(other.hash)) - return false; - if (node == null) { - if (other.node != null) - return false; - } else if (!node.equals(other.node)) - return false; - if (version == null) { - if (other.version != null) - return false; - } else if (!version.equals(other.version)) - return false; - return true; - } - - @Override - public String toString() { - return hash + ":" + node + "#" + version; - } + private static final String SHA1_METHOD = "sha-1"; + + public static final String DIRECT_REQUEST_METHOD = "com.xabber.android.data.extension.Capability.DIRECT_REQUEST"; + + /** + * Hash mehdod. + */ + private final String hash; + + /** + * Product code. + */ + private final String node; + + /** + * Hashed capabilities. + */ + private final String version; + + public Capability(String account, String user, String hash, String node, + String version) { + super((isLegacy(hash) || isSupportedHash(hash)) ? null : account, + (isLegacy(hash) || isSupportedHash(hash)) ? null : user); + this.hash = hash; + this.node = node; + this.version = version; + } + + /** + * @return Hash method. + */ + public String getHash() { + return hash; + } + + /** + * @return Product code. + */ + public String getNode() { + return node; + } + + /** + * @return Hashed capabilities. + */ + public String getVersion() { + return version; + } + + /** + * @param value + * @return hasded value using hash method. + */ + public String getHashedValue(String value) { + try { + MessageDigest md = MessageDigest.getInstance(hash.toUpperCase()); + byte[] digest = md.digest(value.getBytes()); + return Base64.encodeBytes(digest); + } catch (NoSuchAlgorithmException nsae) { + return null; + } + } + + /** + * @param hash + * @return Whether hash method is supported. + */ + private static boolean isSupportedHash(String hash) { + return SHA1_METHOD.equals(hash); + } + + /** + * @return Whether hash method is supported. + */ + public boolean isSupportedHash() { + return isSupportedHash(hash); + } + + /** + * @param hash + * @return Whether this is legacy capability. + */ + private static boolean isLegacy(String hash) { + return hash == null; + } + + /** + * @return Whether this is legacy capability. + */ + public boolean isLegacy() { + return isLegacy(hash); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + ((hash == null) ? 0 : hash.hashCode()); + result = prime * result + ((node == null) ? 0 : node.hashCode()); + result = prime * result + ((version == null) ? 0 : version.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + Capability other = (Capability) obj; + if (hash == null) { + if (other.hash != null) + return false; + } else if (!hash.equals(other.hash)) + return false; + if (node == null) { + if (other.node != null) + return false; + } else if (!node.equals(other.node)) + return false; + if (version == null) { + if (other.version != null) + return false; + } else if (!version.equals(other.version)) + return false; + return true; + } + + @Override + public String toString() { + return hash + ":" + node + "#" + version; + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/capability/ClientInfo.java b/app/src/main/java/com/xabber/android/data/extension/capability/ClientInfo.java index 325cdd7803..e5b3654613 100644 --- a/app/src/main/java/com/xabber/android/data/extension/capability/ClientInfo.java +++ b/app/src/main/java/com/xabber/android/data/extension/capability/ClientInfo.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,44 +20,43 @@ /** * Represent information about client. - * + * * @author alexander.ivanov - * */ public class ClientInfo { - private final String type; + private final String type; - private final String name; + private final String name; - private final Collection features; + private final Collection features; - private final ClientSoftware clientSoftware; + private final ClientSoftware clientSoftware; - public ClientInfo(String type, String name, String node, - Collection features) { - super(); - this.type = type; - this.name = name; - this.features = Collections - .unmodifiableCollection(new ArrayList(features)); - this.clientSoftware = ClientSoftware.getByName(name, node); - } + public ClientInfo(String type, String name, String node, + Collection features) { + super(); + this.type = type; + this.name = name; + this.features = Collections + .unmodifiableCollection(new ArrayList(features)); + this.clientSoftware = ClientSoftware.getByName(name, node); + } - public String getType() { - return type; - } + public String getType() { + return type; + } - public String getName() { - return name; - } + public String getName() { + return name; + } - public ClientSoftware getClientSoftware() { - return clientSoftware; - } + public ClientSoftware getClientSoftware() { + return clientSoftware; + } - public Collection getFeatures() { - return features; - } + public Collection getFeatures() { + return features; + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/capability/ClientSoftware.java b/app/src/main/java/com/xabber/android/data/extension/capability/ClientSoftware.java index 69c1a0cdc9..555504dce5 100644 --- a/app/src/main/java/com/xabber/android/data/extension/capability/ClientSoftware.java +++ b/app/src/main/java/com/xabber/android/data/extension/capability/ClientSoftware.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,54 +18,54 @@ public enum ClientSoftware { - adium("(?iu).*Adium.*"), + adium("(?iu).*Adium.*"), - empathy("(?iu).*Telepathy.*"), + empathy("(?iu).*Telepathy.*"), - gajim("(?iu).*Gajim.*"), + gajim("(?iu).*Gajim.*"), - gtalk("(?iu).*Google.*Talk.*"), + gtalk("(?iu).*Google.*Talk.*"), - ichat("(?iu).*imagent.*"), + ichat("(?iu).*imagent.*"), - miranda("(?iu).*Miranda.*"), + miranda("(?iu).*Miranda.*"), - pidgin("(?iu).*Pidgin.*"), + pidgin("(?iu).*Pidgin.*"), - psi("(?iu).*Psi.*"), + psi("(?iu).*Psi.*"), - qip("(?iu).*QIP.*"), + qip("(?iu).*QIP.*"), - vip("(?iu).*Xabber.*VIP.*"), + vip("(?iu).*Xabber.*VIP.*"), - xabber("(?iu).*Xabber.*"), + xabber("(?iu).*Xabber.*"), - unknown(".*"); + unknown(".*"); - private Pattern regularExpression; + private Pattern regularExpression; - private final static Pattern GTALK_NODE = Pattern - .compile("(?iu).*mail\\.google\\.com.*client.*"); + private final static Pattern GTALK_NODE = Pattern + .compile("(?iu).*mail\\.google\\.com.*client.*"); - private ClientSoftware(String regularExpression) { - this.regularExpression = Pattern.compile(regularExpression); - } + ClientSoftware(String regularExpression) { + this.regularExpression = Pattern.compile(regularExpression); + } - /** - * @param name - * @param node - * @return Client software for given identity name. - */ - public static ClientSoftware getByName(String name, String node) { - if (name == null) { - if (node != null && GTALK_NODE.matcher(node).matches()) - return gtalk; - else - return unknown; - } - for (ClientSoftware clientSoftware : values()) - if (clientSoftware.regularExpression.matcher(name).matches()) - return clientSoftware; - throw new IllegalStateException(); - } + /** + * @param name + * @param node + * @return Client software for given identity name. + */ + public static ClientSoftware getByName(String name, String node) { + if (name == null) { + if (node != null && GTALK_NODE.matcher(node).matches()) + return gtalk; + else + return unknown; + } + for (ClientSoftware clientSoftware : values()) + if (clientSoftware.regularExpression.matcher(name).matches()) + return clientSoftware; + throw new IllegalStateException(); + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/capability/DiscoverInfoRequest.java b/app/src/main/java/com/xabber/android/data/extension/capability/DiscoverInfoRequest.java index 1318d22363..0931a36e4d 100644 --- a/app/src/main/java/com/xabber/android/data/extension/capability/DiscoverInfoRequest.java +++ b/app/src/main/java/com/xabber/android/data/extension/capability/DiscoverInfoRequest.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,29 +18,28 @@ /** * Information about discovery info request. - * + * * @author alexander.ivanov - * */ class DiscoverInfoRequest extends BaseEntity { - private final String packetId; + private final String packetId; - private final Capability capability; + private final Capability capability; - public DiscoverInfoRequest(String account, String user, String packetId, - Capability capability) { - super(account, user); - this.packetId = packetId; - this.capability = capability; - } + public DiscoverInfoRequest(String account, String user, String packetId, + Capability capability) { + super(account, user); + this.packetId = packetId; + this.capability = capability; + } - public String getPacketId() { - return packetId; - } + public String getPacketId() { + return packetId; + } - public Capability getCapability() { - return capability; - } + public Capability getCapability() { + return capability; + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/capability/OnServerInfoReceivedListener.java b/app/src/main/java/com/xabber/android/data/extension/capability/OnServerInfoReceivedListener.java index 3172710f97..c6c513d6f8 100644 --- a/app/src/main/java/com/xabber/android/data/extension/capability/OnServerInfoReceivedListener.java +++ b/app/src/main/java/com/xabber/android/data/extension/capability/OnServerInfoReceivedListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -19,17 +19,16 @@ /** * Listen for server discovery information to be received. - * + * * @author alexander.ivanov - * */ public interface OnServerInfoReceivedListener extends BaseManagerInterface { - /** - * Server discovery information has been received. - * - * @param connection - */ - void onServerInfoReceived(ConnectionItem connection); + /** + * Server discovery information has been received. + * + * @param connection + */ + void onServerInfoReceived(ConnectionItem connection); } diff --git a/app/src/main/java/com/xabber/android/data/extension/capability/ServerInfoManager.java b/app/src/main/java/com/xabber/android/data/extension/capability/ServerInfoManager.java index fc941f1f47..4820b6684d 100644 --- a/app/src/main/java/com/xabber/android/data/extension/capability/ServerInfoManager.java +++ b/app/src/main/java/com/xabber/android/data/extension/capability/ServerInfoManager.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -36,89 +36,89 @@ import com.xabber.xmpp.address.Jid; public class ServerInfoManager implements OnAuthorizedListener, - OnResponseListener { - - /** - * Protocols supported by accounts. - */ - private final Map> protocols; - - private final static ServerInfoManager instance; - - static { - instance = new ServerInfoManager(); - Application.getInstance().addManager(instance); - } - - public static ServerInfoManager getInstance() { - return instance; - } - - private ServerInfoManager() { - super(); - protocols = new HashMap>(); - } - - @Override - public void onAuthorized(ConnectionItem connection) { - if (connection instanceof AccountItem) { - String account = ((AccountItem) connection).getAccount(); - if (protocols.get(account) == null) { - DiscoverInfo packet = new DiscoverInfo(); - packet.setTo(Jid.getServer(account)); - packet.setType(Type.GET); - try { - ConnectionManager.getInstance().sendRequest(account, - packet, this); - } catch (NetworkException e) { - } - return; - } - } - onAvailable(connection); - } - - private void onAvailable(ConnectionItem connection) { - for (OnServerInfoReceivedListener listener : Application.getInstance() - .getManagers(OnServerInfoReceivedListener.class)) - listener.onServerInfoReceived(connection); - } - - @Override - public void onReceived(String account, String packetId, IQ iq) { - if (!(iq instanceof DiscoverInfo)) { - onError(account, packetId, iq); - return; - } - ArrayList features = new ArrayList(); - DiscoverInfo discoverInfo = (DiscoverInfo) iq; - Iterator iterator = discoverInfo.getFeatures(); - while (iterator.hasNext()) - features.add(iterator.next().getVar()); - protocols.put(account, features); - onAvailable(AccountManager.getInstance().getAccount(account)); - } - - @Override - public void onError(String account, String packetId, IQ iq) { - protocols.put(account, new ArrayList()); - onAvailable(AccountManager.getInstance().getAccount(account)); - } - - @Override - public void onTimeout(String account, String packetId) { - onError(account, packetId, null); - } - - @Override - public void onDisconnect(String account, String packetId) { - } - - public boolean isProtocolSupported(String account, String feature) { - Collection collection = protocols.get(account); - if (collection == null) - return false; - return collection.contains(feature); - } + OnResponseListener { + + /** + * Protocols supported by accounts. + */ + private final Map> protocols; + + private final static ServerInfoManager instance; + + static { + instance = new ServerInfoManager(); + Application.getInstance().addManager(instance); + } + + public static ServerInfoManager getInstance() { + return instance; + } + + private ServerInfoManager() { + super(); + protocols = new HashMap>(); + } + + @Override + public void onAuthorized(ConnectionItem connection) { + if (connection instanceof AccountItem) { + String account = ((AccountItem) connection).getAccount(); + if (protocols.get(account) == null) { + DiscoverInfo packet = new DiscoverInfo(); + packet.setTo(Jid.getServer(account)); + packet.setType(Type.GET); + try { + ConnectionManager.getInstance().sendRequest(account, + packet, this); + } catch (NetworkException e) { + } + return; + } + } + onAvailable(connection); + } + + private void onAvailable(ConnectionItem connection) { + for (OnServerInfoReceivedListener listener : Application.getInstance() + .getManagers(OnServerInfoReceivedListener.class)) + listener.onServerInfoReceived(connection); + } + + @Override + public void onReceived(String account, String packetId, IQ iq) { + if (!(iq instanceof DiscoverInfo)) { + onError(account, packetId, iq); + return; + } + ArrayList features = new ArrayList(); + DiscoverInfo discoverInfo = (DiscoverInfo) iq; + Iterator iterator = discoverInfo.getFeatures(); + while (iterator.hasNext()) + features.add(iterator.next().getVar()); + protocols.put(account, features); + onAvailable(AccountManager.getInstance().getAccount(account)); + } + + @Override + public void onError(String account, String packetId, IQ iq) { + protocols.put(account, new ArrayList()); + onAvailable(AccountManager.getInstance().getAccount(account)); + } + + @Override + public void onTimeout(String account, String packetId) { + onError(account, packetId, null); + } + + @Override + public void onDisconnect(String account, String packetId) { + } + + public boolean isProtocolSupported(String account, String feature) { + Collection collection = protocols.get(account); + if (collection == null) + return false; + return collection.contains(feature); + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/cs/ChatStateManager.java b/app/src/main/java/com/xabber/android/data/extension/cs/ChatStateManager.java index 533db1b3d1..574b08a07f 100644 --- a/app/src/main/java/com/xabber/android/data/extension/cs/ChatStateManager.java +++ b/app/src/main/java/com/xabber/android/data/extension/cs/ChatStateManager.java @@ -1,33 +1,19 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data.extension.cs; -import java.util.Calendar; -import java.util.Map; - -import org.jivesoftware.smack.Connection; -import org.jivesoftware.smack.ConnectionCreationListener; -import org.jivesoftware.smack.packet.Message; -import org.jivesoftware.smack.packet.Packet; -import org.jivesoftware.smack.packet.PacketExtension; -import org.jivesoftware.smack.packet.Presence; -import org.jivesoftware.smack.packet.Presence.Type; -import org.jivesoftware.smackx.ChatState; -import org.jivesoftware.smackx.ServiceDiscoveryManager; -import org.jivesoftware.smackx.packet.ChatStateExtension; - import android.app.AlarmManager; import android.app.PendingIntent; import android.content.Context; @@ -52,313 +38,326 @@ import com.xabber.android.receiver.ComposingPausedReceiver; import com.xabber.xmpp.address.Jid; +import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.ConnectionCreationListener; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smack.packet.PacketExtension; +import org.jivesoftware.smack.packet.Presence; +import org.jivesoftware.smack.packet.Presence.Type; +import org.jivesoftware.smackx.ChatState; +import org.jivesoftware.smackx.ServiceDiscoveryManager; +import org.jivesoftware.smackx.packet.ChatStateExtension; + +import java.util.Calendar; +import java.util.Map; + /** * Provide information about chat state. - * + * * @author alexander.ivanov - * */ public class ChatStateManager implements OnDisconnectListener, - OnPacketListener, OnCloseListener { + OnPacketListener, OnCloseListener { - private final static ChatStateManager instance; + private final static ChatStateManager instance; - private static final int PAUSE_TIMEOUT = 30 * 1000; + private static final int PAUSE_TIMEOUT = 30 * 1000; - private static final long REMOVE_STATE_DELAY = 10 * 1000; + private static final long REMOVE_STATE_DELAY = 10 * 1000; - static { - instance = new ChatStateManager(); - Application.getInstance().addManager(instance); + static { + instance = new ChatStateManager(); + Application.getInstance().addManager(instance); - Connection - .addConnectionCreationListener(new ConnectionCreationListener() { - @Override - public void connectionCreated(final Connection connection) { - ServiceDiscoveryManager - .getInstanceFor(connection) - .addFeature( - "http://jabber.org/protocol/chatstates"); - } - }); - } + Connection + .addConnectionCreationListener(new ConnectionCreationListener() { + @Override + public void connectionCreated(final Connection connection) { + ServiceDiscoveryManager + .getInstanceFor(connection) + .addFeature( + "http://jabber.org/protocol/chatstates"); + } + }); + } - public static ChatStateManager getInstance() { - return instance; - } + public static ChatStateManager getInstance() { + return instance; + } - /** - * Chat states for lower cased resource for bareAddress in account. - */ - private final NestedNestedMaps chatStates; + /** + * Chat states for lower cased resource for bareAddress in account. + */ + private final NestedNestedMaps chatStates; - /** - * Cleaners for chat states for lower cased resource for bareAddress in - * account. - */ - private final NestedNestedMaps stateCleaners; + /** + * Cleaners for chat states for lower cased resource for bareAddress in + * account. + */ + private final NestedNestedMaps stateCleaners; - /** - * Information about chat state notification support for lower cased - * resource for bareAddress in account. - */ - private final NestedNestedMaps supports; + /** + * Information about chat state notification support for lower cased + * resource for bareAddress in account. + */ + private final NestedNestedMaps supports; - /** - * Sent chat state notifications for bareAddress in account. - */ - private final NestedMap sent; + /** + * Sent chat state notifications for bareAddress in account. + */ + private final NestedMap sent; - /** - * Scheduled pause intents for bareAddress in account. - */ - private final NestedMap pauseIntents; + /** + * Scheduled pause intents for bareAddress in account. + */ + private final NestedMap pauseIntents; - /** - * Alarm manager. - */ - private final AlarmManager alarmManager; + /** + * Alarm manager. + */ + private final AlarmManager alarmManager; - /** - * Handler for clear states on timeout. - */ - private final Handler handler; + /** + * Handler for clear states on timeout. + */ + private final Handler handler; - private ChatStateManager() { - chatStates = new NestedNestedMaps(); - stateCleaners = new NestedNestedMaps(); - supports = new NestedNestedMaps(); - sent = new NestedMap(); - pauseIntents = new NestedMap(); - alarmManager = (AlarmManager) Application.getInstance() - .getSystemService(Context.ALARM_SERVICE); - handler = new Handler(); - } + private ChatStateManager() { + chatStates = new NestedNestedMaps(); + stateCleaners = new NestedNestedMaps(); + supports = new NestedNestedMaps(); + sent = new NestedMap(); + pauseIntents = new NestedMap(); + alarmManager = (AlarmManager) Application.getInstance() + .getSystemService(Context.ALARM_SERVICE); + handler = new Handler(); + } - /** - * Returns best information chat state for specified bare address. - * - * @param account - * @param bareAddress - * @return null if there is no available information. - */ - public ChatState getChatState(String account, String bareAddress) { - Map map = chatStates.get(account, bareAddress); - if (map == null) - return null; - ChatState chatState = null; - for (ChatState check : map.values()) - if (chatState == null || check.compareTo(chatState) < 0) - chatState = check; - return chatState; - } + /** + * Returns best information chat state for specified bare address. + * + * @param account + * @param bareAddress + * @return null if there is no available information. + */ + public ChatState getChatState(String account, String bareAddress) { + Map map = chatStates.get(account, bareAddress); + if (map == null) + return null; + ChatState chatState = null; + for (ChatState check : map.values()) + if (chatState == null || check.compareTo(chatState) < 0) + chatState = check; + return chatState; + } - /** - * Whether sending chat notification for specified chat is supported. - * - * @param chat - * @param outgoingMessage - * @return - */ - private boolean isSupported(AbstractChat chat, boolean outgoingMessage) { - if (chat instanceof RoomChat) - return false; - String to = chat.getTo(); - String bareAddress = Jid.getBareAddress(to); - String resource = Jid.getResource(to); - Map map = supports.get(chat.getAccount(), bareAddress); - if (map != null) { - if (!"".equals(resource)) { - Boolean value = map.get(resource); - if (value != null) - return value; - } else { - if (outgoingMessage) - return true; - for (Boolean value : map.values()) - if (value != null && value) - return true; - } - } - return outgoingMessage; - } + /** + * Whether sending chat notification for specified chat is supported. + * + * @param chat + * @param outgoingMessage + * @return + */ + private boolean isSupported(AbstractChat chat, boolean outgoingMessage) { + if (chat instanceof RoomChat) + return false; + String to = chat.getTo(); + String bareAddress = Jid.getBareAddress(to); + String resource = Jid.getResource(to); + Map map = supports.get(chat.getAccount(), bareAddress); + if (map != null) { + if (!"".equals(resource)) { + Boolean value = map.get(resource); + if (value != null) + return value; + } else { + if (outgoingMessage) + return true; + for (Boolean value : map.values()) + if (value != null && value) + return true; + } + } + return outgoingMessage; + } - /** - * Update outgoing message before sending. - * - * @param chat - * @param message - */ - public void updateOutgoingMessage(AbstractChat chat, Message message) { - if (!isSupported(chat, true)) - return; - message.addExtension(new ChatStateExtension(ChatState.active)); - sent.put(chat.getAccount(), chat.getUser(), ChatState.active); - cancelPauseIntent(chat.getAccount(), chat.getUser()); - } + /** + * Update outgoing message before sending. + * + * @param chat + * @param message + */ + public void updateOutgoingMessage(AbstractChat chat, Message message) { + if (!isSupported(chat, true)) + return; + message.addExtension(new ChatStateExtension(ChatState.active)); + sent.put(chat.getAccount(), chat.getUser(), ChatState.active); + cancelPauseIntent(chat.getAccount(), chat.getUser()); + } - /** - * Update chat state information and send message if necessary. - * - * @param account - * @param user - * @param chatState - */ - private void updateChatState(String account, String user, - ChatState chatState) { - if (!SettingsManager.chatsStateNotification() - || sent.get(account, user) == chatState) - return; - AbstractChat chat = MessageManager.getInstance().getChat(account, user); - if (chat == null || !isSupported(chat, false)) - return; - sent.put(chat.getAccount(), chat.getUser(), chatState); - Message message = new Message(); - message.setType(chat.getType()); - message.setTo(chat.getTo()); - message.addExtension(new ChatStateExtension(chatState)); - try { - ConnectionManager.getInstance().sendPacket(account, message); - } catch (NetworkException e) { - // Just ignore it. - } - } + /** + * Update chat state information and send message if necessary. + * + * @param account + * @param user + * @param chatState + */ + private void updateChatState(String account, String user, + ChatState chatState) { + if (!SettingsManager.chatsStateNotification() + || sent.get(account, user) == chatState) + return; + AbstractChat chat = MessageManager.getInstance().getChat(account, user); + if (chat == null || !isSupported(chat, false)) + return; + sent.put(chat.getAccount(), chat.getUser(), chatState); + Message message = new Message(); + message.setType(chat.getType()); + message.setTo(chat.getTo()); + message.addExtension(new ChatStateExtension(chatState)); + try { + ConnectionManager.getInstance().sendPacket(account, message); + } catch (NetworkException e) { + // Just ignore it. + } + } - /** - * Cancel pause intent from the schedule. - * - * @param account - * @param user - */ - private void cancelPauseIntent(String account, String user) { - PendingIntent pendingIntent = pauseIntents.remove(account, user); - if (pendingIntent != null) - alarmManager.cancel(pendingIntent); - } + /** + * Cancel pause intent from the schedule. + * + * @param account + * @param user + */ + private void cancelPauseIntent(String account, String user) { + PendingIntent pendingIntent = pauseIntents.remove(account, user); + if (pendingIntent != null) + alarmManager.cancel(pendingIntent); + } - /** - * Must be call each time user change text message. - * - * @param accunt - * @param user - */ - public void onComposing(String account, String user, CharSequence text) { - cancelPauseIntent(account, user); - if (text.length() == 0) { - updateChatState(account, user, ChatState.active); - return; - } else - updateChatState(account, user, ChatState.composing); - Intent intent = ComposingPausedReceiver.createIntent( - Application.getInstance(), account, user); - PendingIntent pendingIntent = PendingIntent.getBroadcast( - Application.getInstance(), 0, intent, 0); - Calendar calendar = Calendar.getInstance(); - calendar.setTimeInMillis(System.currentTimeMillis()); - calendar.add(Calendar.MILLISECOND, PAUSE_TIMEOUT); - alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), - pendingIntent); - pauseIntents.put(account, user, pendingIntent); - } + /** + * Must be call each time user change text message. + * + * @param accunt + * @param user + */ + public void onComposing(String account, String user, CharSequence text) { + cancelPauseIntent(account, user); + if (text.length() == 0) { + updateChatState(account, user, ChatState.active); + return; + } else + updateChatState(account, user, ChatState.composing); + Intent intent = ComposingPausedReceiver.createIntent( + Application.getInstance(), account, user); + PendingIntent pendingIntent = PendingIntent.getBroadcast( + Application.getInstance(), 0, intent, 0); + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(System.currentTimeMillis()); + calendar.add(Calendar.MILLISECOND, PAUSE_TIMEOUT); + alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), + pendingIntent); + pauseIntents.put(account, user, pendingIntent); + } - public void onPaused(Intent intent, String account, String user) { - if (account == null || user == null) - return; - updateChatState(account, user, ChatState.paused); - pauseIntents.remove(account, user); - } + public void onPaused(String account, String user) { + if (account == null || user == null) + return; + updateChatState(account, user, ChatState.paused); + pauseIntents.remove(account, user); + } - @Override - public void onDisconnect(ConnectionItem connection) { - if (!(connection instanceof AccountItem)) - return; - String account = ((AccountItem) connection).getAccount(); - chatStates.clear(account); - for (Map map : stateCleaners.getNested(account) - .values()) - for (Runnable runnable : map.values()) - handler.removeCallbacks(runnable); - stateCleaners.clear(account); - supports.clear(account); - sent.clear(account); - for (PendingIntent pendingIntent : pauseIntents.getNested(account) - .values()) - alarmManager.cancel(pendingIntent); - pauseIntents.clear(account); - } + @Override + public void onDisconnect(ConnectionItem connection) { + if (!(connection instanceof AccountItem)) + return; + String account = ((AccountItem) connection).getAccount(); + chatStates.clear(account); + for (Map map : stateCleaners.getNested(account) + .values()) + for (Runnable runnable : map.values()) + handler.removeCallbacks(runnable); + stateCleaners.clear(account); + supports.clear(account); + sent.clear(account); + for (PendingIntent pendingIntent : pauseIntents.getNested(account) + .values()) + alarmManager.cancel(pendingIntent); + pauseIntents.clear(account); + } - private void removeCallback(String account, String bareAddress, - String resource) { - Runnable runnable = stateCleaners - .remove(account, bareAddress, resource); - if (runnable != null) - handler.removeCallbacks(runnable); - } + private void removeCallback(String account, String bareAddress, + String resource) { + Runnable runnable = stateCleaners + .remove(account, bareAddress, resource); + if (runnable != null) + handler.removeCallbacks(runnable); + } - @Override - public void onPacket(ConnectionItem connection, final String bareAddress, - Packet packet) { - if (!(connection instanceof AccountItem)) - return; - final String resource = Jid.getResource(packet.getFrom()); - if (resource == null) - return; - final String account = ((AccountItem) connection).getAccount(); - if (packet instanceof Presence) { - Presence presence = (Presence) packet; - if (presence.getType() != Type.unavailable) - return; - chatStates.remove(account, bareAddress, resource); - removeCallback(account, bareAddress, resource); - supports.remove(account, bareAddress, resource); - } else if (packet instanceof Message) { - boolean support = false; - for (PacketExtension extension : packet.getExtensions()) - if (extension instanceof ChatStateExtension) { - removeCallback(account, bareAddress, resource); - ChatState chatState = ((ChatStateExtension) extension) - .getState(); - chatStates.put(account, bareAddress, resource, chatState); - if (chatState != ChatState.active) { - Runnable runnable = new Runnable() { - @Override - public void run() { - if (this != stateCleaners.get(account, - bareAddress, resource)) - return; - chatStates.remove(account, bareAddress, - resource); - removeCallback(account, bareAddress, resource); - RosterManager.getInstance().onContactChanged( - account, bareAddress); - } - }; - handler.postDelayed(runnable, REMOVE_STATE_DELAY); - stateCleaners.put(account, bareAddress, resource, - runnable); - } - RosterManager.getInstance().onContactChanged(account, - bareAddress); - support = true; - break; - } - Message message = (Message) packet; - if (message.getType() != Message.Type.chat - && message.getType() != Message.Type.groupchat) - return; - if (support) - supports.put(account, bareAddress, resource, true); - else if (supports.get(account, bareAddress, resource) == null) - // Disable only if there no information about support. - supports.put(account, bareAddress, resource, false); - } - } + @Override + public void onPacket(ConnectionItem connection, final String bareAddress, + Packet packet) { + if (!(connection instanceof AccountItem)) + return; + final String resource = Jid.getResource(packet.getFrom()); + if (resource == null) + return; + final String account = ((AccountItem) connection).getAccount(); + if (packet instanceof Presence) { + Presence presence = (Presence) packet; + if (presence.getType() != Type.unavailable) + return; + chatStates.remove(account, bareAddress, resource); + removeCallback(account, bareAddress, resource); + supports.remove(account, bareAddress, resource); + } else if (packet instanceof Message) { + boolean support = false; + for (PacketExtension extension : packet.getExtensions()) + if (extension instanceof ChatStateExtension) { + removeCallback(account, bareAddress, resource); + ChatState chatState = ((ChatStateExtension) extension) + .getState(); + chatStates.put(account, bareAddress, resource, chatState); + if (chatState != ChatState.active) { + Runnable runnable = new Runnable() { + @Override + public void run() { + if (this != stateCleaners.get(account, + bareAddress, resource)) + return; + chatStates.remove(account, bareAddress, + resource); + removeCallback(account, bareAddress, resource); + RosterManager.getInstance().onContactChanged( + account, bareAddress); + } + }; + handler.postDelayed(runnable, REMOVE_STATE_DELAY); + stateCleaners.put(account, bareAddress, resource, + runnable); + } + RosterManager.getInstance().onContactChanged(account, + bareAddress); + support = true; + break; + } + Message message = (Message) packet; + if (message.getType() != Message.Type.chat + && message.getType() != Message.Type.groupchat) + return; + if (support) + supports.put(account, bareAddress, resource, true); + else if (supports.get(account, bareAddress, resource) == null) + // Disable only if there no information about support. + supports.put(account, bareAddress, resource, false); + } + } - @Override - public void onClose() { - for (PendingIntent pendingIntent : pauseIntents.values()) - alarmManager.cancel(pendingIntent); - pauseIntents.clear(); - } + @Override + public void onClose() { + for (PendingIntent pendingIntent : pauseIntents.values()) + alarmManager.cancel(pendingIntent); + pauseIntents.clear(); + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/muc/MUCManager.java b/app/src/main/java/com/xabber/android/data/extension/muc/MUCManager.java index 83065482fd..d88d18359e 100644 --- a/app/src/main/java/com/xabber/android/data/extension/muc/MUCManager.java +++ b/app/src/main/java/com/xabber/android/data/extension/muc/MUCManager.java @@ -1,32 +1,22 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data.extension.muc; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; - -import org.jivesoftware.smack.XMPPConnection; -import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smack.packet.Message; -import org.jivesoftware.smack.packet.Packet; -import org.jivesoftware.smackx.muc.MultiUserChat; -import org.jivesoftware.smackx.packet.MUCUser; - import android.database.Cursor; +import com.xabber.android.R; import com.xabber.android.data.Application; import com.xabber.android.data.LogManager; import com.xabber.android.data.NetworkException; @@ -43,442 +33,422 @@ import com.xabber.android.data.notification.EntityNotificationProvider; import com.xabber.android.data.notification.NotificationManager; import com.xabber.android.data.roster.RosterManager; -import com.xabber.androiddev.R; import com.xabber.xmpp.muc.MUC; +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smackx.muc.MultiUserChat; +import org.jivesoftware.smackx.packet.MUCUser; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; + /** * Manage multi user chats. - * + *

* Warning: We are going to remove SMACK components. - * + * * @author alexander.ivanov - * */ public class MUCManager implements OnLoadListener, OnPacketListener { - private final EntityNotificationProvider inviteProvider; - - private final EntityNotificationProvider authorizationErrorProvider; - - private final static MUCManager instance; - - static { - instance = new MUCManager(); - Application.getInstance().addManager(instance); - } - - public static MUCManager getInstance() { - return instance; - } - - private MUCManager() { - inviteProvider = new EntityNotificationProvider( - R.drawable.ic_stat_subscribe); - authorizationErrorProvider = new EntityNotificationProvider( - R.drawable.ic_stat_auth_failed); - } - - @Override - public void onLoad() { - final Collection roomChats = new ArrayList(); - final Collection needJoins = new ArrayList(); - Cursor cursor = RoomTable.getInstance().list(); - try { - if (cursor.moveToFirst()) { - do { - RoomChat roomChat = new RoomChat( - RoomTable.getAccount(cursor), - RoomTable.getRoom(cursor), - RoomTable.getNickname(cursor), - RoomTable.getPassword(cursor)); - if (RoomTable.needJoin(cursor)) - needJoins.add(roomChat); - roomChats.add(roomChat); - } while (cursor.moveToNext()); - } - } finally { - cursor.close(); - } - Application.getInstance().runOnUiThread(new Runnable() { - @Override - public void run() { - onLoaded(roomChats, needJoins); - } - }); - } - - private void onLoaded(Collection roomChats, - Collection needJoins) { - for (RoomChat roomChat : roomChats) { - AbstractChat abstractChat = MessageManager.getInstance().getChat( - roomChat.getAccount(), roomChat.getUser()); - if (abstractChat != null) - MessageManager.getInstance().removeChat(abstractChat); - MessageManager.getInstance().addChat(roomChat); - if (needJoins.contains(roomChat)) - roomChat.setState(RoomState.waiting); - } - NotificationManager.getInstance().registerNotificationProvider( - inviteProvider); - NotificationManager.getInstance().registerNotificationProvider( - authorizationErrorProvider); - } - - /** - * @param account - * @param room - * @return null if does not exists. - */ - private RoomChat getRoomChat(String account, String room) { - AbstractChat chat = MessageManager.getInstance().getChat(account, room); - if (chat != null && chat instanceof RoomChat) - return (RoomChat) chat; - return null; - } - - /** - * @param account - * @param room - * @return Whether there is such room. - */ - public boolean hasRoom(String account, String room) { - return getRoomChat(account, room) != null; - } - - /** - * @param account - * @param room - * @return nickname or empty string if room does not exists. - */ - public String getNickname(String account, String room) { - RoomChat roomChat = getRoomChat(account, room); - if (roomChat == null) - return ""; - return roomChat.getNickname(); - } - - /** - * @param account - * @param room - * @return password or empty string if room does not exists. - */ - public String getPassword(String account, String room) { - RoomChat roomChat = getRoomChat(account, room); - if (roomChat == null) - return ""; - return roomChat.getPassword(); - } - - /** - * @param account - * @param room - * @return list of occupants or empty list. - */ - public Collection getOccupants(String account, String room) { - RoomChat roomChat = getRoomChat(account, room); - if (roomChat == null) - return Collections.emptyList(); - return roomChat.getOccupants(); - } - - /** - * @param account - * @param room - * @return null if there is no such invite. - */ - public RoomInvite getInvite(String account, String room) { - return inviteProvider.get(account, room); - } - - public void removeInvite(RoomInvite abstractRequest) { - inviteProvider.remove(abstractRequest); - } - - public void removeRoom(final String account, final String room) { - removeInvite(getInvite(account, room)); - RoomChat roomChat = getRoomChat(account, room); - if (roomChat == null) - return; - leaveRoom(account, room); - MessageManager.getInstance().removeChat(roomChat); - RosterManager.getInstance().onContactChanged(account, room); - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - RoomTable.getInstance().remove(account, room); - } - }); - } - - /** - * Creates or updates existed room. - * - * @param account - * @param room - * @param nickname - * @param password - */ - public void createRoom(String account, String room, String nickname, - String password, boolean join) { - removeInvite(getInvite(account, room)); - AbstractChat chat = MessageManager.getInstance().getChat(account, room); - RoomChat roomChat; - if (chat == null || !(chat instanceof RoomChat)) { - if (chat != null) - MessageManager.getInstance().removeChat(chat); - roomChat = new RoomChat(account, room, nickname, password); - MessageManager.getInstance().addChat(roomChat); - } else { - roomChat = (RoomChat) chat; - roomChat.setNickname(nickname); - roomChat.setPassword(password); - } - requestToWriteRoom(account, room, nickname, password, join); - if (join) - joinRoom(account, room, true); - } - - private void requestToWriteRoom(final String account, final String room, - final String nickname, final String password, final boolean join) { - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - RoomTable.getInstance().write(account, room, nickname, - password, join); - } - }); - } - - /** - * @param account - * @param room - * @return Whether room is disabled. - */ - public boolean isDisabled(final String account, final String room) { - RoomChat roomChat = getRoomChat(account, room); - return roomChat == null || roomChat.getState() == RoomState.unavailable; - } - - /** - * @param account - * @param room - * @return Whether connected is establish or connection is in progress. - */ - public boolean inUse(final String account, final String room) { - RoomChat roomChat = getRoomChat(account, room); - return roomChat != null && roomChat.getState().inUse(); - } - - /** - * Requests to join to the room. - * - * @param account - * @param room - * @param requested - * Whether user request to join the room. - */ - public void joinRoom(final String account, final String room, - boolean requested) { - final XMPPConnection xmppConnection; - final RoomChat roomChat; - final String nickname; - final String password; - final Thread thread; - roomChat = getRoomChat(account, room); - if (roomChat == null) { - Application.getInstance().onError(R.string.ENTRY_IS_NOT_FOUND); - return; - } - RoomState state = roomChat.getState(); - if (state == RoomState.available || state == RoomState.occupation) { - Application.getInstance().onError(R.string.ALREADY_JOINED); - return; - } - if (state == RoomState.creating || state == RoomState.joining) { - Application.getInstance().onError(R.string.ALREADY_IN_PROGRESS); - return; - } - nickname = roomChat.getNickname(); - password = roomChat.getPassword(); - requestToWriteRoom(account, room, nickname, password, true); - ConnectionThread connectionThread = AccountManager.getInstance() - .getAccount(account).getConnectionThread(); - if (connectionThread == null) { - Application.getInstance().onError(R.string.NOT_CONNECTED); - return; - } - xmppConnection = connectionThread.getXMPPConnection(); - if (xmppConnection == null) { - Application.getInstance().onError(R.string.NOT_CONNECTED); - return; - } - final MultiUserChat multiUserChat; - try { - multiUserChat = new MultiUserChat(xmppConnection, room); - } catch (IllegalStateException e) { - Application.getInstance().onError(R.string.NOT_CONNECTED); - return; - } - roomChat.setState(RoomState.joining); - roomChat.setMultiUserChat(multiUserChat); - roomChat.setRequested(requested); - thread = new Thread("Join to room " + room + " from " + account) { - @Override - public void run() { - try { - if (roomChat.getMultiUserChat() != multiUserChat) - return; - multiUserChat.join(nickname, password); - Application.getInstance().runOnUiThread(new Runnable() { - @Override - public void run() { - if (roomChat.getMultiUserChat() != multiUserChat) - return; - if (roomChat.getState() == RoomState.joining) - roomChat.setState(RoomState.occupation); - removeAuthorizationError(account, room); - RosterManager.getInstance().onContactChanged( - account, room); - } - }); - return; - } catch (final XMPPException e) { - Application.getInstance().runOnUiThread(new Runnable() { - @Override - public void run() { - if (roomChat.getMultiUserChat() != multiUserChat) - return; - roomChat.setState(RoomState.error); - addAuthorizationError(account, room); - if (e.getXMPPError() != null - && e.getXMPPError().getCode() == 409) - Application.getInstance().onError( - R.string.NICK_ALREADY_USED); - else if (e.getXMPPError() != null - && e.getXMPPError().getCode() == 401) - Application.getInstance().onError( - R.string.AUTHENTICATION_FAILED); - else - Application.getInstance().onError( - R.string.NOT_CONNECTED); - RosterManager.getInstance().onContactChanged( - account, room); - } - }); - return; - } catch (IllegalStateException e) { - } catch (RuntimeException e) { - LogManager.exception(this, e); - } catch (Exception e) { - LogManager.exception(this, e); - } - Application.getInstance().runOnUiThread(new Runnable() { - @Override - public void run() { - if (roomChat.getMultiUserChat() != multiUserChat) - return; - roomChat.setState(RoomState.waiting); - Application.getInstance().onError( - R.string.NOT_CONNECTED); - RosterManager.getInstance().onContactChanged(account, - room); - } - }); - } - }; - thread.setDaemon(true); - thread.start(); - } - - public void leaveRoom(String account, String room) { - final MultiUserChat multiUserChat; - RoomChat roomChat = getRoomChat(account, room); - if (roomChat == null) - return; - multiUserChat = roomChat.getMultiUserChat(); - roomChat.setState(RoomState.unavailable); - roomChat.setRequested(false); - roomChat.newAction(roomChat.getNickname(), null, ChatAction.leave); - requestToWriteRoom(account, room, roomChat.getNickname(), - roomChat.getPassword(), false); - if (multiUserChat != null) { - Thread thread = new Thread("Leave to room " + room + " from " - + account) { - @Override - public void run() { - try { - multiUserChat.leave(); - } catch (IllegalStateException e) { - // Do nothing - } - } - }; - thread.setDaemon(true); - thread.start(); - } - RosterManager.getInstance().onContactChanged(account, room); - } - - @Override - public void onPacket(ConnectionItem connection, String bareAddress, - Packet packet) { - if (!(connection instanceof AccountItem)) - return; - String account = ((AccountItem) connection).getAccount(); - if (bareAddress == null || !(packet instanceof Message)) - return; - Message message = (Message) packet; - if (message.getType() != Message.Type.normal - && message.getType() != Message.Type.chat) - return; - MUCUser mucUser = MUC.getMUCUserExtension(packet); - if (mucUser == null || mucUser.getInvite() == null) - return; - RoomChat roomChat = getRoomChat(account, bareAddress); - if (roomChat == null || !roomChat.getState().inUse()) { - String inviter = mucUser.getInvite().getFrom(); - if (inviter == null) - inviter = bareAddress; - inviteProvider.add(new RoomInvite(account, bareAddress, inviter, - mucUser.getInvite().getReason(), mucUser.getPassword()), - true); - } - } - - /** - * Sends invitation. - * - * @param account - * @param room - * @param user - * @throws NetworkException - */ - public void invite(String account, String room, String user) - throws NetworkException { - RoomChat roomChat = getRoomChat(account, room); - if (roomChat == null || roomChat.getState() != RoomState.available) { - Application.getInstance().onError(R.string.NOT_CONNECTED); - return; - } - Message message = new Message(room); - MUCUser mucUser = new MUCUser(); - MUCUser.Invite invite = new MUCUser.Invite(); - invite.setTo(user); - invite.setReason(""); - mucUser.setInvite(invite); - message.addExtension(mucUser); - ConnectionManager.getInstance().sendPacket(account, message); - roomChat.putInvite(message.getPacketID(), user); - roomChat.newAction(roomChat.getNickname(), user, ChatAction.invite_sent); - } - - public void removeAuthorizationError(String account, String room) { - authorizationErrorProvider.remove(account, room); - } - - public void addAuthorizationError(String account, String room) { - authorizationErrorProvider.add( - new RoomAuthorizationError(account, room), null); - } + private final static MUCManager instance; + + static { + instance = new MUCManager(); + Application.getInstance().addManager(instance); + } + + private final EntityNotificationProvider inviteProvider; + private final EntityNotificationProvider authorizationErrorProvider; + + private MUCManager() { + inviteProvider = new EntityNotificationProvider<>(R.drawable.ic_stat_add_circle); + authorizationErrorProvider = new EntityNotificationProvider<>(R.drawable.ic_stat_error); + } + + public static MUCManager getInstance() { + return instance; + } + + @Override + public void onLoad() { + final Collection roomChats = new ArrayList<>(); + final Collection needJoins = new ArrayList<>(); + Cursor cursor = RoomTable.getInstance().list(); + try { + if (cursor.moveToFirst()) { + do { + RoomChat roomChat = new RoomChat( + RoomTable.getAccount(cursor), RoomTable.getRoom(cursor), + RoomTable.getNickname(cursor), RoomTable.getPassword(cursor)); + if (RoomTable.needJoin(cursor)) { + needJoins.add(roomChat); + } + roomChats.add(roomChat); + } while (cursor.moveToNext()); + } + } finally { + cursor.close(); + } + Application.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + onLoaded(roomChats, needJoins); + } + }); + } + + private void onLoaded(Collection roomChats, Collection needJoins) { + for (RoomChat roomChat : roomChats) { + AbstractChat abstractChat = MessageManager.getInstance().getChat( + roomChat.getAccount(), roomChat.getUser()); + if (abstractChat != null) { + MessageManager.getInstance().removeChat(abstractChat); + } + MessageManager.getInstance().addChat(roomChat); + if (needJoins.contains(roomChat)) { + roomChat.setState(RoomState.waiting); + } + } + NotificationManager.getInstance().registerNotificationProvider(inviteProvider); + NotificationManager.getInstance().registerNotificationProvider(authorizationErrorProvider); + } + + /** + * @return null if does not exists. + */ + private RoomChat getRoomChat(String account, String room) { + AbstractChat chat = MessageManager.getInstance().getChat(account, room); + if (chat != null && chat instanceof RoomChat) { + return (RoomChat) chat; + } + return null; + } + + /** + * @return Whether there is such room. + */ + public boolean hasRoom(String account, String room) { + return getRoomChat(account, room) != null; + } + + /** + * @return nickname or empty string if room does not exists. + */ + public String getNickname(String account, String room) { + RoomChat roomChat = getRoomChat(account, room); + if (roomChat == null) { + return ""; + } + return roomChat.getNickname(); + } + + /** + * @param account + * @param room + * @return password or empty string if room does not exists. + */ + public String getPassword(String account, String room) { + RoomChat roomChat = getRoomChat(account, room); + if (roomChat == null) { + return ""; + } + return roomChat.getPassword(); + } + + /** + * @return list of occupants or empty list. + */ + public Collection getOccupants(String account, String room) { + RoomChat roomChat = getRoomChat(account, room); + if (roomChat == null) { + return Collections.emptyList(); + } + return roomChat.getOccupants(); + } + + /** + * @return null if there is no such invite. + */ + public RoomInvite getInvite(String account, String room) { + return inviteProvider.get(account, room); + } + + public void removeInvite(RoomInvite abstractRequest) { + inviteProvider.remove(abstractRequest); + } + + public void removeRoom(final String account, final String room) { + removeInvite(getInvite(account, room)); + RoomChat roomChat = getRoomChat(account, room); + if (roomChat == null) { + return; + } + leaveRoom(account, room); + MessageManager.getInstance().removeChat(roomChat); + RosterManager.getInstance().onContactChanged(account, room); + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + RoomTable.getInstance().remove(account, room); + } + }); + } + + /** + * Creates or updates existed room. + * + */ + public void createRoom(String account, String room, String nickname, + String password, boolean join) { + removeInvite(getInvite(account, room)); + AbstractChat chat = MessageManager.getInstance().getChat(account, room); + RoomChat roomChat; + if (chat == null || !(chat instanceof RoomChat)) { + if (chat != null) { + MessageManager.getInstance().removeChat(chat); + } + roomChat = new RoomChat(account, room, nickname, password); + MessageManager.getInstance().addChat(roomChat); + } else { + roomChat = (RoomChat) chat; + roomChat.setNickname(nickname); + roomChat.setPassword(password); + } + requestToWriteRoom(account, room, nickname, password, join); + if (join) { + joinRoom(account, room, true); + } + } + + private void requestToWriteRoom(final String account, final String room, + final String nickname, final String password, final boolean join) { + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + RoomTable.getInstance().write(account, room, nickname, + password, join); + } + }); + } + + /** + * @return Whether room is disabled. + */ + public boolean isDisabled(final String account, final String room) { + RoomChat roomChat = getRoomChat(account, room); + return roomChat == null || roomChat.getState() == RoomState.unavailable; + } + + /** + * @return Whether connected is establish or connection is in progress. + */ + public boolean inUse(final String account, final String room) { + RoomChat roomChat = getRoomChat(account, room); + return roomChat != null && roomChat.getState().inUse(); + } + + /** + * Requests to join to the room. + * + * @param requested Whether user request to join the room. + */ + public void joinRoom(final String account, final String room, boolean requested) { + final XMPPConnection xmppConnection; + final RoomChat roomChat; + final String nickname; + final String password; + final Thread thread; + roomChat = getRoomChat(account, room); + if (roomChat == null) { + Application.getInstance().onError(R.string.ENTRY_IS_NOT_FOUND); + return; + } + RoomState state = roomChat.getState(); + if (state == RoomState.available || state == RoomState.occupation) { + Application.getInstance().onError(R.string.ALREADY_JOINED); + return; + } + if (state == RoomState.creating || state == RoomState.joining) { + Application.getInstance().onError(R.string.ALREADY_IN_PROGRESS); + return; + } + nickname = roomChat.getNickname(); + password = roomChat.getPassword(); + requestToWriteRoom(account, room, nickname, password, true); + ConnectionThread connectionThread = AccountManager.getInstance() + .getAccount(account).getConnectionThread(); + if (connectionThread == null) { + Application.getInstance().onError(R.string.NOT_CONNECTED); + return; + } + xmppConnection = connectionThread.getXMPPConnection(); + if (xmppConnection == null) { + Application.getInstance().onError(R.string.NOT_CONNECTED); + return; + } + final MultiUserChat multiUserChat; + try { + multiUserChat = new MultiUserChat(xmppConnection, room); + } catch (IllegalStateException e) { + Application.getInstance().onError(R.string.NOT_CONNECTED); + return; + } + roomChat.setState(RoomState.joining); + roomChat.setMultiUserChat(multiUserChat); + roomChat.setRequested(requested); + thread = new Thread("Join to room " + room + " from " + account) { + @Override + public void run() { + try { + if (roomChat.getMultiUserChat() != multiUserChat) { + return; + } + multiUserChat.join(nickname, password); + Application.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + if (roomChat.getMultiUserChat() != multiUserChat) { + return; + } + if (roomChat.getState() == RoomState.joining) { + roomChat.setState(RoomState.occupation); + } + removeAuthorizationError(account, room); + RosterManager.getInstance().onContactChanged(account, room); + } + }); + return; + } catch (final XMPPException e) { + Application.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + if (roomChat.getMultiUserChat() != multiUserChat) { + return; + } + roomChat.setState(RoomState.error); + addAuthorizationError(account, room); + if (e.getXMPPError() != null && e.getXMPPError().getCode() == 409) { + Application.getInstance().onError(R.string.NICK_ALREADY_USED); + } else if (e.getXMPPError() != null && e.getXMPPError().getCode() == 401) { + Application.getInstance().onError(R.string.AUTHENTICATION_FAILED); + } else { + Application.getInstance().onError(R.string.NOT_CONNECTED); + } + RosterManager.getInstance().onContactChanged(account, room); + } + }); + return; + } catch (IllegalStateException e) { + } catch (Exception e) { + LogManager.exception(this, e); + } + Application.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + if (roomChat.getMultiUserChat() != multiUserChat) { + return; + } + roomChat.setState(RoomState.waiting); + Application.getInstance().onError(R.string.NOT_CONNECTED); + RosterManager.getInstance().onContactChanged(account, room); + } + }); + } + }; + thread.setDaemon(true); + thread.start(); + } + + public void leaveRoom(String account, String room) { + final MultiUserChat multiUserChat; + RoomChat roomChat = getRoomChat(account, room); + if (roomChat == null) { + return; + } + multiUserChat = roomChat.getMultiUserChat(); + roomChat.setState(RoomState.unavailable); + roomChat.setRequested(false); + roomChat.newAction(roomChat.getNickname(), null, ChatAction.leave); + requestToWriteRoom(account, room, roomChat.getNickname(), roomChat.getPassword(), false); + if (multiUserChat != null) { + Thread thread = new Thread("Leave to room " + room + " from " + account) { + @Override + public void run() { + try { + multiUserChat.leave(); + } catch (IllegalStateException e) { + // Do nothing + } + } + }; + thread.setDaemon(true); + thread.start(); + } + RosterManager.getInstance().onContactChanged(account, room); + } + + @Override + public void onPacket(ConnectionItem connection, String bareAddress, Packet packet) { + if (!(connection instanceof AccountItem)) { + return; + } + String account = ((AccountItem) connection).getAccount(); + if (bareAddress == null || !(packet instanceof Message)) { + return; + } + Message message = (Message) packet; + if (message.getType() != Message.Type.normal && message.getType() != Message.Type.chat) { + return; + } + MUCUser mucUser = MUC.getMUCUserExtension(packet); + if (mucUser == null || mucUser.getInvite() == null) { + return; + } + RoomChat roomChat = getRoomChat(account, bareAddress); + if (roomChat == null || !roomChat.getState().inUse()) { + String inviter = mucUser.getInvite().getFrom(); + if (inviter == null) { + inviter = bareAddress; + } + inviteProvider.add(new RoomInvite(account, bareAddress, inviter, + mucUser.getInvite().getReason(), mucUser.getPassword()), true); + } + } + + /** + * Sends invitation. + * + * @throws NetworkException + */ + public void invite(String account, String room, String user) throws NetworkException { + RoomChat roomChat = getRoomChat(account, room); + if (roomChat == null || roomChat.getState() != RoomState.available) { + Application.getInstance().onError(R.string.NOT_CONNECTED); + return; + } + Message message = new Message(room); + MUCUser mucUser = new MUCUser(); + MUCUser.Invite invite = new MUCUser.Invite(); + invite.setTo(user); + invite.setReason(""); + mucUser.setInvite(invite); + message.addExtension(mucUser); + ConnectionManager.getInstance().sendPacket(account, message); + roomChat.putInvite(message.getPacketID(), user); + roomChat.newAction(roomChat.getNickname(), user, ChatAction.invite_sent); + } + + public void removeAuthorizationError(String account, String room) { + authorizationErrorProvider.remove(account, room); + } + + public void addAuthorizationError(String account, String room) { + authorizationErrorProvider.add(new RoomAuthorizationError(account, room), null); + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/muc/Occupant.java b/app/src/main/java/com/xabber/android/data/extension/muc/Occupant.java index aca4466418..509ea2d824 100644 --- a/app/src/main/java/com/xabber/android/data/extension/muc/Occupant.java +++ b/app/src/main/java/com/xabber/android/data/extension/muc/Occupant.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,81 +20,81 @@ /** * Room occupant. - * + * * @author alexander.ivanov - * */ public class Occupant implements Comparable { - private final String nickname; + private final String nickname; - private String jid; + private String jid; - private Role role; + private Role role; - private Affiliation affiliation; + private Affiliation affiliation; - private StatusMode statusMode; + private StatusMode statusMode; - private String statusText; + private String statusText; - public Occupant(String nickname) { - this.nickname = nickname; - } + public Occupant(String nickname) { + this.nickname = nickname; + } - public String getNickname() { - return nickname; - } + public String getNickname() { + return nickname; + } - /** - * @return can be null. - */ - public String getJid() { - return jid; - } + /** + * @return can be null. + */ + public String getJid() { + return jid; + } - public void setJid(String jid) { - this.jid = jid; - } + public void setJid(String jid) { + this.jid = jid; + } - public Role getRole() { - return role; - } + public Role getRole() { + return role; + } - public void setRole(Role role) { - this.role = role; - } + public void setRole(Role role) { + this.role = role; + } - public Affiliation getAffiliation() { - return affiliation; - } + public Affiliation getAffiliation() { + return affiliation; + } - public void setAffiliation(Affiliation affiliation) { - this.affiliation = affiliation; - } + public void setAffiliation(Affiliation affiliation) { + this.affiliation = affiliation; + } - public StatusMode getStatusMode() { - return statusMode; - } + public StatusMode getStatusMode() { + return statusMode; + } - public void setStatusMode(StatusMode statusMode) { - this.statusMode = statusMode; - } + public void setStatusMode(StatusMode statusMode) { + this.statusMode = statusMode; + } - public String getStatusText() { - return statusText; - } + public String getStatusText() { + return statusText; + } - public void setStatusText(String statusText) { - this.statusText = statusText; - } + public void setStatusText(String statusText) { + this.statusText = statusText; + } - @Override - public int compareTo(Occupant another) { - int result = another.role.ordinal() - role.ordinal(); - if (result != 0) - return result; - return nickname.compareTo(another.nickname); - } + @Override + public int compareTo(Occupant another) { + int result = another.role.ordinal() - role.ordinal(); + if (result != 0) { + return result; + } + return nickname.compareTo(another.nickname); + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/muc/RoomAuthorizationError.java b/app/src/main/java/com/xabber/android/data/extension/muc/RoomAuthorizationError.java index eb80f04955..a939cf6172 100644 --- a/app/src/main/java/com/xabber/android/data/extension/muc/RoomAuthorizationError.java +++ b/app/src/main/java/com/xabber/android/data/extension/muc/RoomAuthorizationError.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,34 +16,31 @@ import android.content.Intent; +import com.xabber.android.R; import com.xabber.android.data.Application; import com.xabber.android.data.entity.BaseEntity; import com.xabber.android.data.notification.EntityNotificationItem; -import com.xabber.android.ui.MUCEditor; -import com.xabber.androiddev.R; - -public class RoomAuthorizationError extends BaseEntity implements - EntityNotificationItem { - - public RoomAuthorizationError(String account, String user) { - super(account, user); - } - - @Override - public Intent getIntent() { - return MUCEditor.createIntent(Application.getInstance(), - account, user); - } - - @Override - public String getTitle() { - return user; - } - - @Override - public String getText() { - return Application.getInstance().getString( - R.string.AUTHENTICATION_FAILED); - } +import com.xabber.android.ui.ConferenceAdd; + +public class RoomAuthorizationError extends BaseEntity implements EntityNotificationItem { + + public RoomAuthorizationError(String account, String user) { + super(account, user); + } + + @Override + public Intent getIntent() { + return ConferenceAdd.createIntent(Application.getInstance(), account, user); + } + + @Override + public String getTitle() { + return user; + } + + @Override + public String getText() { + return Application.getInstance().getString(R.string.AUTHENTICATION_FAILED); + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/muc/RoomChat.java b/app/src/main/java/com/xabber/android/data/extension/muc/RoomChat.java index 2825ec4cfc..c449d4f0e7 100644 --- a/app/src/main/java/com/xabber/android/data/extension/muc/RoomChat.java +++ b/app/src/main/java/com/xabber/android/data/extension/muc/RoomChat.java @@ -1,34 +1,20 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data.extension.muc; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.NoSuchElementException; - -import org.jivesoftware.smack.packet.Message; -import org.jivesoftware.smack.packet.Message.Type; -import org.jivesoftware.smack.packet.Packet; -import org.jivesoftware.smack.packet.Presence; -import org.jivesoftware.smack.util.StringUtils; -import org.jivesoftware.smackx.muc.MultiUserChat; -import org.jivesoftware.smackx.packet.MUCUser; - +import com.xabber.android.R; import com.xabber.android.data.Application; import com.xabber.android.data.SettingsManager; import com.xabber.android.data.SettingsManager.ChatsShowStatusChange; @@ -36,454 +22,461 @@ import com.xabber.android.data.message.AbstractChat; import com.xabber.android.data.message.ChatAction; import com.xabber.android.data.message.MessageItem; +import com.xabber.android.data.message.chat.ChatManager; import com.xabber.android.data.roster.RosterManager; -import com.xabber.androiddev.R; import com.xabber.xmpp.address.Jid; import com.xabber.xmpp.delay.Delay; import com.xabber.xmpp.muc.Affiliation; import com.xabber.xmpp.muc.MUC; import com.xabber.xmpp.muc.Role; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.packet.Message.Type; +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smack.packet.Presence; +import org.jivesoftware.smack.util.StringUtils; +import org.jivesoftware.smackx.muc.MultiUserChat; +import org.jivesoftware.smackx.packet.MUCUser; + +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.NoSuchElementException; + /** * Chat room. - * + *

* Warning: We are going to remove SMACK components. - * + * * @author alexander.ivanov - * */ public class RoomChat extends AbstractChat { - /** - * Joining was requested from the UI. - */ - private boolean requested; - - /** - * Nickname used in the room. - */ - private String nickname; - - private String password; - private RoomState state; - private String subject; - - /** - * SMACK MUC implementation. - */ - private MultiUserChat multiUserChat; - - /** - * Information about occupants for STRING-PREPed resource. - */ - private final Map occupants; - - /** - * Invited user for the sent packet ID. - */ - private final Map invites; - - RoomChat(String account, String user, String nickname, String password) { - super(account, user); - this.nickname = nickname; - this.password = password; - requested = false; - state = RoomState.unavailable; - subject = ""; - multiUserChat = null; - occupants = new HashMap(); - invites = new HashMap(); - } - - @Override - public String getTo() { - return user; - } - - @Override - public Type getType() { - return Type.groupchat; - } - - String getRoom() { - return user; - } - - String getNickname() { - return nickname; - } - - void setNickname(String nickname) { - this.nickname = nickname; - } - - String getPassword() { - return password; - } - - void setPassword(String password) { - this.password = password; - } - - boolean isRequested() { - return requested; - } - - void setRequested(boolean requested) { - this.requested = requested; - } - - public RoomState getState() { - return state; - } - - void setState(RoomState state) { - this.state = state; - if (!state.inUse()) { - multiUserChat = null; - occupants.clear(); - invites.clear(); - } - if (state == RoomState.available) - sendMessages(); - } - - Collection getOccupants() { - return Collections.unmodifiableCollection(occupants.values()); - } - - String getSubject() { - return subject; - } - - MultiUserChat getMultiUserChat() { - return multiUserChat; - } - - void setMultiUserChat(MultiUserChat multiUserChat) { - this.multiUserChat = multiUserChat; - } - - void putInvite(String packetID, String user) { - invites.put(packetID, user); - } - - @Override - protected MessageItem newMessage(String text) { - return newMessage(nickname, text, null, null, false, false, false, - false, true); - } - - @Override - protected boolean canSendMessage() { - if (super.canSendMessage()) - return state == RoomState.available; - return false; - } - - @Override - protected boolean notifyAboutMessage() { - return SettingsManager.eventsMessage() == SettingsManager.EventsMessage.chatAndMuc; - } - - @Override - protected boolean onPacket(String bareAddress, Packet packet) { - if (!super.onPacket(bareAddress, packet)) - return false; - final String from = packet.getFrom(); - final String resource = StringUtils.parseResource(from); - if (packet instanceof Message) { - final Message message = (Message) packet; - if (message.getType() == Message.Type.error) { - String invite = invites.remove(message.getPacketID()); - if (invite != null) { - newAction(nickname, invite, ChatAction.invite_error); - } - return true; - } - MUCUser mucUser = MUC.getMUCUserExtension(packet); - if (mucUser != null && mucUser.getDecline() != null) { - onInvitationDeclined(mucUser.getDecline().getFrom(), mucUser - .getDecline().getReason()); - return true; - } - final String text = message.getBody(); - final String subject = message.getSubject(); - if (text == null && subject == null) - return true; - if (isSelf(resource)) // Own message - return true; - if (subject != null) { - if (this.subject.equals(subject)) - return true; - this.subject = subject; - RosterManager.getInstance().onContactChanged(account, - bareAddress); - newAction(resource, subject, ChatAction.subject); - } else { - String packetID = message.getPacketID(); - Date delay = Delay.getDelay(message); - for (MessageItem messageItem : messages) { - // Search for duplicates - if (packetID != null - && packetID.equals(messageItem.getPacketID())) - return true; - if (delay != null) { - if (delay.equals(messageItem.getDelayTimestamp()) - && resource.equals(messageItem.getResource()) - && text.equals(messageItem.getText())) - return true; - } - } - updateThreadId(message.getThread()); - MessageItem messageItem = newMessage(resource, text, null, - delay, true, true, false, false, true); - messageItem.setPacketID(packetID); - } - } else if (packet instanceof Presence) { - String stringPrep = Jid.getStringPrep(resource); - Presence presence = (Presence) packet; - if (presence.getType() == Presence.Type.available) { - Occupant oldOccupant = occupants.get(stringPrep); - Occupant newOccupant = createOccupant(resource, presence); - occupants.put(stringPrep, newOccupant); - if (oldOccupant == null) { - onAvailable(resource); - RosterManager.getInstance().onContactChanged(account, user); - } else { - boolean changed = false; - if (oldOccupant.getAffiliation() != newOccupant - .getAffiliation()) { - changed = true; - onAffiliationChanged(resource, - newOccupant.getAffiliation()); - } - if (oldOccupant.getRole() != newOccupant.getRole()) { - changed = true; - onRoleChanged(resource, newOccupant.getRole()); - } - if (oldOccupant.getStatusMode() != newOccupant - .getStatusMode() - || !oldOccupant.getStatusText().equals( - newOccupant.getStatusText())) { - changed = true; - onStatusChanged(resource, newOccupant.getStatusMode(), - newOccupant.getStatusText()); - } - if (changed) - RosterManager.getInstance().onContactChanged(account, - user); - } - } else if (presence.getType() == Presence.Type.unavailable - && state == RoomState.available) { - occupants.remove(stringPrep); - MUCUser mucUser = MUC.getMUCUserExtension(presence); - if (mucUser != null && mucUser.getStatus() != null) { - String code = mucUser.getStatus().getCode(); - if ("307".equals(code)) { - onKick(resource, mucUser.getItem().getActor()); - } else if ("301".equals(code)) { - onBan(resource, mucUser.getItem().getActor()); - } else if ("303".equals(code)) { - String newNick = mucUser.getItem().getNick(); - if (newNick == null) - return true; - onRename(resource, newNick); - Occupant occupant = createOccupant(newNick, presence); - occupants.put(Jid.getStringPrep(newNick), occupant); - } else if ("321".equals(code)) { - onRevoke(resource, mucUser.getItem().getActor()); - } - } else { - onLeave(resource); - } - RosterManager.getInstance().onContactChanged(account, user); - } - } - return true; - } - - /** - * @return Whether status change action should be added to the chat history. - */ - private boolean showStatusChange() { - return SettingsManager.chatsShowStatusChange() != ChatsShowStatusChange.never; - } - - /** - * @param resource - * @return Whether resource is own nickname. - */ - private boolean isSelf(String resource) { - return Jid.getStringPrep(nickname).equals(Jid.getStringPrep(resource)); - } - - /** - * Informs that the invitee has declined the invitation. - * - * @param from - * @param reason - */ - private void onInvitationDeclined(String from, String reason) { - // TODO - } - - /** - * A occupant becomes available. - * - * @param resource - */ - private void onAvailable(String resource) { - if (isSelf(resource)) { - setState(RoomState.available); - if (isRequested()) { - if (showStatusChange()) - newMessage( - resource, - Application.getInstance().getString( - R.string.action_join_complete_to, user), - ChatAction.complete, null, true, true, false, - false, true); - active = true; - setRequested(false); - } else { - if (showStatusChange()) - newAction(resource, null, ChatAction.complete); - } - } else { - if (state == RoomState.available) - if (showStatusChange()) - newAction(resource, null, ChatAction.join); - } - } - - /** - * Warning: this method should be placed with packet provider. - * - * @param resource - * @param presence - * @return New occupant based on presence information. - */ - private Occupant createOccupant(String resource, Presence presence) { - Occupant occupant = new Occupant(resource); - String jid = null; - Affiliation affiliation = Affiliation.none; - Role role = Role.none; - StatusMode statusMode = StatusMode.unavailable; - String statusText = null; - MUCUser mucUser = MUC.getMUCUserExtension(presence); - if (mucUser != null) { - MUCUser.Item item = mucUser.getItem(); - if (item != null) { - jid = item.getJid(); - try { - affiliation = Affiliation.fromString(item.getAffiliation()); - } catch (NoSuchElementException e) { - } - try { - role = Role.fromString(item.getRole()); - } catch (NoSuchElementException e) { - } - statusMode = StatusMode.createStatusMode(presence); - statusText = presence.getStatus(); - } - } - if (statusText == null) - statusText = ""; - occupant.setJid(jid); - occupant.setAffiliation(affiliation); - occupant.setRole(role); - occupant.setStatusMode(statusMode); - occupant.setStatusText(statusText); - return occupant; - } - - private void onAffiliationChanged(String resource, Affiliation affiliation) { - } - - private void onRoleChanged(String resource, Role role) { - } - - private void onStatusChanged(String resource, StatusMode statusMode, - String statusText) { - } - - /** - * A occupant leaves room. - * - * @param resource - */ - private void onLeave(String resource) { - if (showStatusChange()) - newAction(resource, null, ChatAction.leave); - if (isSelf(resource)) { - setState(RoomState.waiting); - RosterManager.getInstance().onContactChanged(account, user); - } - } - - /** - * A occupant was kicked. - * - * @param resource - * @param actor - */ - private void onKick(String resource, String actor) { - if (showStatusChange()) - newAction(resource, actor, ChatAction.kick); - if (isSelf(resource)) - MUCManager.getInstance().leaveRoom(account, user); - } - - /** - * A occupant was banned. - * - * @param resource - * @param actor - */ - private void onBan(String resource, String actor) { - if (showStatusChange()) - newAction(resource, actor, ChatAction.ban); - if (isSelf(resource)) - MUCManager.getInstance().leaveRoom(account, user); - } - - /** - * A occupant has changed his nickname in the room. - * - * @param resource - * @param newNick - */ - private void onRename(String resource, String newNick) { - if (showStatusChange()) - newAction(resource, newNick, ChatAction.nickname); - } - - /** - * A user's membership was revoked from the room - * - * @param resource - * @param actor - */ - private void onRevoke(String resource, String actor) { - if (showStatusChange()) - newAction(resource, actor, ChatAction.kick); - if (isSelf(resource)) - MUCManager.getInstance().leaveRoom(account, user); - } - - @Override - protected void onComplete() { - super.onComplete(); - if (getState() == RoomState.waiting) - MUCManager.getInstance().joinRoom(account, user, false); - } - - @Override - protected void onDisconnect() { - super.onDisconnect(); - if (state != RoomState.unavailable) - setState(RoomState.waiting); - } + /** + * Information about occupants for STRING-PREPed resource. + */ + private final Map occupants; + /** + * Invited user for the sent packet ID. + */ + private final Map invites; + /** + * Joining was requested from the UI. + */ + private boolean requested; + /** + * Nickname used in the room. + */ + private String nickname; + private String password; + private RoomState state; + private String subject; + /** + * SMACK MUC implementation. + */ + private MultiUserChat multiUserChat; + + RoomChat(String account, String user, String nickname, String password) { + super(account, user); + this.nickname = nickname; + this.password = password; + requested = false; + state = RoomState.unavailable; + subject = ""; + multiUserChat = null; + occupants = new HashMap<>(); + invites = new HashMap<>(); + } + + @Override + public String getTo() { + return user; + } + + @Override + public Type getType() { + return Type.groupchat; + } + + String getRoom() { + return user; + } + + String getNickname() { + return nickname; + } + + void setNickname(String nickname) { + this.nickname = nickname; + } + + String getPassword() { + return password; + } + + void setPassword(String password) { + this.password = password; + } + + boolean isRequested() { + return requested; + } + + void setRequested(boolean requested) { + this.requested = requested; + } + + public RoomState getState() { + return state; + } + + void setState(RoomState state) { + this.state = state; + if (!state.inUse()) { + multiUserChat = null; + occupants.clear(); + invites.clear(); + } + if (state == RoomState.available) { + sendMessages(); + } + } + + Collection getOccupants() { + return Collections.unmodifiableCollection(occupants.values()); + } + + String getSubject() { + return subject; + } + + MultiUserChat getMultiUserChat() { + return multiUserChat; + } + + void setMultiUserChat(MultiUserChat multiUserChat) { + this.multiUserChat = multiUserChat; + } + + void putInvite(String packetID, String user) { + invites.put(packetID, user); + } + + @Override + protected MessageItem newMessage(String text) { + return newMessage(nickname, text, null, null, false, false, false, false, true); + } + + @Override + protected boolean canSendMessage() { + return super.canSendMessage() && state == RoomState.available; + } + + @Override + protected boolean notifyAboutMessage() { + return SettingsManager.eventsMessage() == SettingsManager.EventsMessage.chatAndMuc; + } + + @Override + protected boolean onPacket(String bareAddress, Packet packet) { + if (!super.onPacket(bareAddress, packet)) { + return false; + } + final String from = packet.getFrom(); + final String resource = StringUtils.parseResource(from); + if (packet instanceof Message) { + final Message message = (Message) packet; + if (message.getType() == Message.Type.error) { + String invite = invites.remove(message.getPacketID()); + if (invite != null) { + newAction(nickname, invite, ChatAction.invite_error); + } + return true; + } + MUCUser mucUser = MUC.getMUCUserExtension(packet); + if (mucUser != null && mucUser.getDecline() != null) { + onInvitationDeclined(mucUser.getDecline().getFrom(), mucUser.getDecline().getReason()); + return true; + } + if (mucUser != null && mucUser.getStatus() != null && mucUser.getStatus().getCode().equals("100") + && ChatManager.getInstance().isSuppress100(account, user)) { + // 'This room is not anonymous' + return true; + } + final String text = message.getBody(); + final String subject = message.getSubject(); + if (text == null && subject == null) { + return true; + } + if (isSelf(resource)) { // Own message + return true; + } + if (subject != null) { + if (this.subject.equals(subject)) { + return true; + } + this.subject = subject; + RosterManager.getInstance().onContactChanged(account, bareAddress); + newAction(resource, subject, ChatAction.subject); + } else { + boolean notify = true; + String packetID = message.getPacketID(); + Date delay = Delay.getDelay(message); + if (delay != null) { + notify = false; + } + for (MessageItem messageItem : messages) { + // Search for duplicates + if (packetID != null && packetID.equals(messageItem.getPacketID())) { + return true; + } + if (delay != null) { + if (delay.equals(messageItem.getDelayTimestamp()) + && resource.equals(messageItem.getResource()) + && text.equals(messageItem.getText())) { + return true; + } + } + } + updateThreadId(message.getThread()); + MessageItem messageItem = newMessage(resource, text, null, + delay, true, notify, false, false, true); + messageItem.setPacketID(packetID); + } + } else if (packet instanceof Presence) { + String stringPrep = Jid.getStringPrep(resource); + Presence presence = (Presence) packet; + if (presence.getType() == Presence.Type.available) { + Occupant oldOccupant = occupants.get(stringPrep); + Occupant newOccupant = createOccupant(resource, presence); + occupants.put(stringPrep, newOccupant); + if (oldOccupant == null) { + onAvailable(resource); + RosterManager.getInstance().onContactChanged(account, user); + } else { + boolean changed = false; + if (oldOccupant.getAffiliation() != newOccupant.getAffiliation()) { + changed = true; + onAffiliationChanged(resource, newOccupant.getAffiliation()); + } + if (oldOccupant.getRole() != newOccupant.getRole()) { + changed = true; + onRoleChanged(resource, newOccupant.getRole()); + } + if (oldOccupant.getStatusMode() != newOccupant.getStatusMode() + || !oldOccupant.getStatusText().equals(newOccupant.getStatusText())) { + changed = true; + onStatusChanged(resource, newOccupant.getStatusMode(), newOccupant.getStatusText()); + } + if (changed) { + RosterManager.getInstance().onContactChanged(account, user); + } + } + } else if (presence.getType() == Presence.Type.unavailable && state == RoomState.available) { + occupants.remove(stringPrep); + MUCUser mucUser = MUC.getMUCUserExtension(presence); + if (mucUser != null && mucUser.getStatus() != null) { + String code = mucUser.getStatus().getCode(); + if ("307".equals(code)) { + onKick(resource, mucUser.getItem().getActor()); + } else if ("301".equals(code)) { + onBan(resource, mucUser.getItem().getActor()); + } else if ("303".equals(code)) { + String newNick = mucUser.getItem().getNick(); + if (newNick == null) { + return true; + } + onRename(resource, newNick); + Occupant occupant = createOccupant(newNick, presence); + occupants.put(Jid.getStringPrep(newNick), occupant); + } else if ("321".equals(code)) { + onRevoke(resource, mucUser.getItem().getActor()); + } + } else { + onLeave(resource); + } + RosterManager.getInstance().onContactChanged(account, user); + } + } + return true; + } + + /** + * @return Whether status change action should be added to the chat history. + */ + private boolean showStatusChange() { + return SettingsManager.chatsShowStatusChange() != ChatsShowStatusChange.never; + } + + /** + * @return Whether resource is own nickname. + */ + private boolean isSelf(String resource) { + return Jid.getStringPrep(nickname).equals(Jid.getStringPrep(resource)); + } + + /** + * Informs that the invitee has declined the invitation. + */ + private void onInvitationDeclined(String from, String reason) { + // TODO + } + + /** + * A occupant becomes available. + */ + private void onAvailable(String resource) { + if (isSelf(resource)) { + setState(RoomState.available); + if (isRequested()) { + if (showStatusChange()) { + newMessage(resource, Application.getInstance().getString( + R.string.action_join_complete_to, user), + ChatAction.complete, null, true, true, false, false, true); + } + active = true; + setRequested(false); + } else { + if (showStatusChange()) { + newAction(resource, null, ChatAction.complete); + } + } + } else { + if (state == RoomState.available) { + if (showStatusChange()) { + newAction(resource, null, ChatAction.join); + } + } + } + } + + /** + * Warning: this method should be placed with packet provider. + * + * @return New occupant based on presence information. + */ + private Occupant createOccupant(String resource, Presence presence) { + Occupant occupant = new Occupant(resource); + String jid = null; + Affiliation affiliation = Affiliation.none; + Role role = Role.none; + StatusMode statusMode = StatusMode.unavailable; + String statusText = null; + MUCUser mucUser = MUC.getMUCUserExtension(presence); + if (mucUser != null) { + MUCUser.Item item = mucUser.getItem(); + if (item != null) { + jid = item.getJid(); + try { + affiliation = Affiliation.fromString(item.getAffiliation()); + } catch (NoSuchElementException e) { + } + try { + role = Role.fromString(item.getRole()); + } catch (NoSuchElementException e) { + } + statusMode = StatusMode.createStatusMode(presence); + statusText = presence.getStatus(); + } + } + if (statusText == null) { + statusText = ""; + } + occupant.setJid(jid); + occupant.setAffiliation(affiliation); + occupant.setRole(role); + occupant.setStatusMode(statusMode); + occupant.setStatusText(statusText); + return occupant; + } + + private void onAffiliationChanged(String resource, Affiliation affiliation) { + } + + private void onRoleChanged(String resource, Role role) { + } + + private void onStatusChanged(String resource, StatusMode statusMode, String statusText) { + } + + /** + * A occupant leaves room. + */ + private void onLeave(String resource) { + if (showStatusChange()) { + newAction(resource, null, ChatAction.leave); + } + if (isSelf(resource)) { + setState(RoomState.waiting); + RosterManager.getInstance().onContactChanged(account, user); + } + } + + /** + * A occupant was kicked. + * + */ + private void onKick(String resource, String actor) { + if (showStatusChange()) { + newAction(resource, actor, ChatAction.kick); + } + if (isSelf(resource)) { + MUCManager.getInstance().leaveRoom(account, user); + } + } + + /** + * A occupant was banned. + * + */ + private void onBan(String resource, String actor) { + if (showStatusChange()) { + newAction(resource, actor, ChatAction.ban); + } + if (isSelf(resource)) { + MUCManager.getInstance().leaveRoom(account, user); + } + } + + /** + * A occupant has changed his nickname in the room. + * + */ + private void onRename(String resource, String newNick) { + if (showStatusChange()) { + newAction(resource, newNick, ChatAction.nickname); + } + } + + /** + * A user's membership was revoked from the room + * + */ + private void onRevoke(String resource, String actor) { + if (showStatusChange()) { + newAction(resource, actor, ChatAction.kick); + } + if (isSelf(resource)) { + MUCManager.getInstance().leaveRoom(account, user); + } + } + + @Override + protected void onComplete() { + super.onComplete(); + if (getState() == RoomState.waiting) { + MUCManager.getInstance().joinRoom(account, user, false); + } + } + + @Override + protected void onDisconnect() { + super.onDisconnect(); + if (state != RoomState.unavailable) { + setState(RoomState.waiting); + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/data/extension/muc/RoomContact.java b/app/src/main/java/com/xabber/android/data/extension/muc/RoomContact.java index 255c16114e..303ff95194 100644 --- a/app/src/main/java/com/xabber/android/data/extension/muc/RoomContact.java +++ b/app/src/main/java/com/xabber/android/data/extension/muc/RoomContact.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -22,43 +22,42 @@ /** * Represents information about room in contact list. - * + *

* {@link #getUser()} will be bare jid. - * + * * @author alexander.ivanov - * */ public class RoomContact extends AbstractContact { - private final RoomChat roomItem; - - public RoomContact(RoomChat roomChat) { - super(roomChat.getAccount(), roomChat.getUser()); - this.roomItem = roomChat; - } - - @Override - public String getStatusText() { - return roomItem.getSubject(); - } - - @Override - public StatusMode getStatusMode() { - return roomItem.getState().toStatusMode(); - } - - @Override - public Drawable getAvatar() { - return AvatarManager.getInstance().getRoomAvatar(user); - } - - @Override - public Drawable getAvatarForContactList() { - return AvatarManager.getInstance().getRoomAvatarForContactList(user); - } - - @Override - public boolean isConnected() { - return roomItem.getState() == RoomState.available; - } + private final RoomChat roomItem; + + public RoomContact(RoomChat roomChat) { + super(roomChat.getAccount(), roomChat.getUser()); + this.roomItem = roomChat; + } + + @Override + public String getStatusText() { + return roomItem.getSubject(); + } + + @Override + public StatusMode getStatusMode() { + return roomItem.getState().toStatusMode(); + } + + @Override + public Drawable getAvatar() { + return AvatarManager.getInstance().getRoomAvatar(user); + } + + @Override + public Drawable getAvatarForContactList() { + return AvatarManager.getInstance().getRoomAvatarForContactList(user); + } + + @Override + public boolean isConnected() { + return roomItem.getState() == RoomState.available; + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/muc/RoomInvite.java b/app/src/main/java/com/xabber/android/data/extension/muc/RoomInvite.java index 029ccfa343..083859dbf4 100644 --- a/app/src/main/java/com/xabber/android/data/extension/muc/RoomInvite.java +++ b/app/src/main/java/com/xabber/android/data/extension/muc/RoomInvite.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,89 +16,85 @@ import android.content.Intent; +import com.xabber.android.R; import com.xabber.android.data.Application; import com.xabber.android.data.account.AccountManager; import com.xabber.android.data.entity.BaseEntity; import com.xabber.android.data.notification.EntityNotificationItem; import com.xabber.android.data.roster.RosterManager; -import com.xabber.android.ui.MUCEditor; -import com.xabber.androiddev.R; +import com.xabber.android.ui.MUCInvite; /** * Invite to join the room. - * + * * @author alexander.ivanov - * */ public class RoomInvite extends BaseEntity implements EntityNotificationItem { - /** - * JID of entity that sent an invitation. - */ - private final String inviter; + /** + * JID of entity that sent an invitation. + */ + private final String inviter; - /** - * Text of invitation. - */ - private final String reason; + /** + * Text of invitation. + */ + private final String reason; - /** - * Password to be used in connection. - */ - private final String password; + /** + * Password to be used in connection. + */ + private final String password; - public RoomInvite(String account, String user, String inviter, - String reason, String password) { - super(account, user); - this.inviter = inviter; - this.reason = reason == null ? "" : reason; - this.password = password == null ? "" : password; - } + public RoomInvite(String account, String user, String inviter, String reason, String password) { + super(account, user); + this.inviter = inviter; + this.reason = reason == null ? "" : reason; + this.password = password == null ? "" : password; + } - @Override - public Intent getIntent() { - return MUCEditor.createInviteIntent(Application.getInstance(), account, - user); - } + @Override + public Intent getIntent() { + return MUCInvite.createIntent(Application.getInstance(), account, user); + } - @Override - public String getText() { - return Application.getInstance().getString(R.string.muc_invite_message); - } + @Override + public String getText() { + return Application.getInstance().getString(R.string.muc_invite_message); + } - @Override - public String getTitle() { - return user; - } + @Override + public String getTitle() { + return user; + } - /** - * @return Text for the confirmation. - */ - public String getConfirmation() { - String accountName = AccountManager.getInstance().getVerboseName( - account); - String inviterName = RosterManager.getInstance().getName(account, - inviter); - if (reason == null || "".equals(reason)) - return Application.getInstance() - .getString(R.string.muc_invite_confirm, accountName, - inviterName, user); - else - return Application.getInstance().getString( - R.string.muc_invite_confirm_reason, accountName, - inviterName, user, reason); - } + /** + * @return Text for the confirmation. + */ + public String getConfirmation() { + String accountName = AccountManager.getInstance().getVerboseName(account); + String inviterName = RosterManager.getInstance().getName(account, inviter); + if (reason == null || "".equals(reason)) { + return Application.getInstance() + .getString(R.string.muc_invite_confirm, accountName, + inviterName, user); + } else { + return Application.getInstance().getString( + R.string.muc_invite_confirm_reason, accountName, + inviterName, user, reason); + } + } - public String getInviter() { - return inviter; - } + public String getInviter() { + return inviter; + } - public String getReason() { - return reason; - } + public String getReason() { + return reason; + } - public String getPassword() { - return password; - } + public String getPassword() { + return password; + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/muc/RoomState.java b/app/src/main/java/com/xabber/android/data/extension/muc/RoomState.java index e60db2e5c4..e9f79c1e5b 100644 --- a/app/src/main/java/com/xabber/android/data/extension/muc/RoomState.java +++ b/app/src/main/java/com/xabber/android/data/extension/muc/RoomState.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,73 +18,84 @@ /** * States of chat room. - * + * * @author alexander.ivanov */ public enum RoomState { - /** - * Room is available. - */ - available, + /** + * Room is available. + */ + available, - /** - * Receiving occupants. - */ - occupation, + /** + * Receiving occupants. + */ + occupation, - /** - * Joining is in progress. - */ - joining, + /** + * Joining is in progress. + */ + joining, - /** - * Creation is in progress. - */ - creating, + /** + * Creation is in progress. + */ + creating, - /** - * Room is unavailable, i.e. not connected or not joined. - */ - unavailable, + /** + * Room is unavailable, i.e. not connected or not joined. + */ + unavailable, - /** - * Waiting for connection to join the room. - */ - waiting, + /** + * Waiting for connection to join the room. + */ + waiting, - /** - * Authentication error. - */ - error; + /** + * Authentication error. + */ + error; - /** - * @return Status mode used in contact list. - */ - StatusMode toStatusMode() { - if (this == RoomState.available) - return StatusMode.available; - else if (this == RoomState.occupation) - return StatusMode.connection; - else if (this == RoomState.joining) - return StatusMode.connection; - else if (this == RoomState.creating) - return StatusMode.connection; - else if (this == RoomState.unavailable) - return StatusMode.unavailable; - else if (this == RoomState.waiting) - return StatusMode.connection; - else if (this == RoomState.error) - return StatusMode.unsubscribed; - else - throw new IllegalStateException(); - } + /** + * @return Status mode used in contact list. + */ + StatusMode toStatusMode() { + switch (this) { + case available: + return StatusMode.available; + case occupation: + case joining: + case creating: + case waiting: + return StatusMode.connection; + case unavailable: + return StatusMode.unavailable; + case error: + return StatusMode.unsubscribed; + default: + throw new IllegalStateException(); + } + } - /** - * @return Connected is establish or connection is in progress. - */ - boolean inUse() { - return this == RoomState.available || this == RoomState.occupation - || this == RoomState.creating || this == RoomState.joining; - } + /** + * @return Connected is establish or connection is in progress. + */ + boolean inUse() { + switch (this) { + + case available: + case occupation: + case joining: + case creating: + return true; + + case unavailable: + case waiting: + case error: + default: + return false; + } + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/muc/RoomTable.java b/app/src/main/java/com/xabber/android/data/extension/muc/RoomTable.java index ca08bac319..02385fa6e9 100644 --- a/app/src/main/java/com/xabber/android/data/extension/muc/RoomTable.java +++ b/app/src/main/java/com/xabber/android/data/extension/muc/RoomTable.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -23,179 +23,166 @@ /** * Storage with settings for the chat rooms. - * + * * @author alexander.ivanov */ class RoomTable extends AbstractAccountTable { - private static final class Fields implements AbstractAccountTable.Fields { - - private Fields() { - } - - /** - * Bare jid of chat room. - */ - public static final String ROOM = "room"; - - /** - * Nick in chat room. - */ - public static final String NICKNAME = "nickname"; - - /** - * Password. - */ - public static final String PASSWORD = "password"; - - /** - * Join on launch. - */ - public static final String NEED_JOIN = "need_join"; - - } - - private static final String NAME = "rooms"; - private static final String[] PROJECTION = new String[] { Fields._ID, - Fields.ACCOUNT, Fields.ROOM, Fields.NICKNAME, Fields.PASSWORD, - Fields.NEED_JOIN, }; - - private final DatabaseManager databaseManager; - private SQLiteStatement writeStatement; - private final Object writeLock; - - private final static RoomTable instance; - - static { - instance = new RoomTable(DatabaseManager.getInstance()); - DatabaseManager.getInstance().addTable(instance); - } - - public static RoomTable getInstance() { - return instance; - } - - private RoomTable(DatabaseManager databaseManager) { - this.databaseManager = databaseManager; - writeStatement = null; - writeLock = new Object(); - } - - @Override - public void create(SQLiteDatabase db) { - String sql; - sql = "CREATE TABLE " + NAME + " (" + Fields._ID - + " INTEGER PRIMARY KEY," + Fields.ACCOUNT + " TEXT," - + Fields.ROOM + " TEXT," + Fields.NICKNAME + " TEXT," - + Fields.PASSWORD + " TEXT," + Fields.NEED_JOIN + " INTEGER);"; - DatabaseManager.execSQL(db, sql); - sql = "CREATE UNIQUE INDEX " + NAME + "_list ON " + NAME + " (" - + Fields.ACCOUNT + ", " + Fields.ROOM + ");"; - DatabaseManager.execSQL(db, sql); - } - - @Override - public void migrate(SQLiteDatabase db, int toVersion) { - super.migrate(db, toVersion); - String sql; - switch (toVersion) { - case 24: - sql = "CREATE TABLE rooms (_id INTEGER PRIMARY KEY," - + "account TEXT," + "room TEXT," + "nickname TEXT," - + "password TEXT," + "timestamp INTEGER);"; - DatabaseManager.execSQL(db, sql); - sql = "CREATE UNIQUE INDEX rooms_list ON rooms (account, room);"; - DatabaseManager.execSQL(db, sql); - break; - case 25: - DatabaseManager.dropTable(db, "rooms"); - sql = "CREATE TABLE rooms (_id INTEGER PRIMARY KEY," - + "account TEXT," + "room TEXT," + "nickname TEXT," - + "password TEXT," + "need_join INTEGER);"; - DatabaseManager.execSQL(db, sql); - sql = "CREATE UNIQUE INDEX rooms_list ON rooms (account, room);"; - DatabaseManager.execSQL(db, sql); - break; - default: - break; - } - } - - /** - * Adds or updates room. - * - * @param account - * @param room - * @param nickname - * @param password - * @param join - */ - void write(String account, String room, String nickname, String password, - boolean join) { - synchronized (writeLock) { - if (writeStatement == null) { - SQLiteDatabase db = databaseManager.getWritableDatabase(); - writeStatement = db - .compileStatement("INSERT OR REPLACE INTO " + NAME - + " (" + Fields.ACCOUNT + ", " + Fields.ROOM - + ", " + Fields.NICKNAME + ", " - + Fields.PASSWORD + ", " + Fields.NEED_JOIN - + ") VALUES (?, ?, ?, ?, ?);"); - } - writeStatement.bindString(1, account); - writeStatement.bindString(2, room); - writeStatement.bindString(3, nickname); - writeStatement.bindString(4, password); - writeStatement.bindLong(5, join ? 1 : 0); - writeStatement.execute(); - } - } - - /** - * Removes room. - * - * @param account - * @param room - */ - void remove(String account, String room) { - SQLiteDatabase db = databaseManager.getWritableDatabase(); - db.delete(NAME, Fields.ACCOUNT + " = ? AND " + Fields.ROOM + " = ?", - new String[] { account, room }); - } - - @Override - protected String getTableName() { - return NAME; - } - - @Override - protected String[] getProjection() { - return PROJECTION; - } - - @Override - protected String getListOrder() { - return Fields.NEED_JOIN; - } - - static long getId(Cursor cursor) { - return cursor.getLong(cursor.getColumnIndex(Fields._ID)); - } - - static String getRoom(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.ROOM)); - } - - static String getNickname(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.NICKNAME)); - } - - static String getPassword(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.PASSWORD)); - } - - static boolean needJoin(Cursor cursor) { - return cursor.getLong(cursor.getColumnIndex(Fields.NEED_JOIN)) != 0; - } + private static final class Fields implements AbstractAccountTable.Fields { + + private Fields() { + } + + /** + * Bare jid of chat room. + */ + public static final String ROOM = "room"; + + /** + * Nick in chat room. + */ + public static final String NICKNAME = "nickname"; + + /** + * Password. + */ + public static final String PASSWORD = "password"; + + /** + * Join on launch. + */ + public static final String NEED_JOIN = "need_join"; + + } + + private static final String NAME = "rooms"; + private static final String[] PROJECTION = new String[]{ Fields._ID, + Fields.ACCOUNT, Fields.ROOM, Fields.NICKNAME, Fields.PASSWORD, Fields.NEED_JOIN }; + + private final DatabaseManager databaseManager; + private SQLiteStatement writeStatement; + private final Object writeLock; + + private final static RoomTable instance; + + static { + instance = new RoomTable(DatabaseManager.getInstance()); + DatabaseManager.getInstance().addTable(instance); + } + + public static RoomTable getInstance() { + return instance; + } + + private RoomTable(DatabaseManager databaseManager) { + this.databaseManager = databaseManager; + writeStatement = null; + writeLock = new Object(); + } + + @Override + public void create(SQLiteDatabase db) { + String sql; + sql = "CREATE TABLE " + NAME + " (" + Fields._ID + + " INTEGER PRIMARY KEY," + Fields.ACCOUNT + " TEXT," + + Fields.ROOM + " TEXT," + Fields.NICKNAME + " TEXT," + + Fields.PASSWORD + " TEXT," + Fields.NEED_JOIN + " INTEGER);"; + DatabaseManager.execSQL(db, sql); + sql = "CREATE UNIQUE INDEX " + NAME + "_list ON " + NAME + " (" + + Fields.ACCOUNT + ", " + Fields.ROOM + ");"; + DatabaseManager.execSQL(db, sql); + } + + @Override + public void migrate(SQLiteDatabase db, int toVersion) { + super.migrate(db, toVersion); + String sql; + switch (toVersion) { + case 24: + sql = "CREATE TABLE rooms (_id INTEGER PRIMARY KEY," + + "account TEXT," + "room TEXT," + "nickname TEXT," + + "password TEXT," + "timestamp INTEGER);"; + DatabaseManager.execSQL(db, sql); + sql = "CREATE UNIQUE INDEX rooms_list ON rooms (account, room);"; + DatabaseManager.execSQL(db, sql); + break; + case 25: + DatabaseManager.dropTable(db, "rooms"); + sql = "CREATE TABLE rooms (_id INTEGER PRIMARY KEY," + + "account TEXT," + "room TEXT," + "nickname TEXT," + + "password TEXT," + "need_join INTEGER);"; + DatabaseManager.execSQL(db, sql); + sql = "CREATE UNIQUE INDEX rooms_list ON rooms (account, room);"; + DatabaseManager.execSQL(db, sql); + break; + default: + break; + } + } + + /** + * Adds or updates room. + */ + void write(String account, String room, String nickname, String password, boolean join) { + synchronized (writeLock) { + if (writeStatement == null) { + SQLiteDatabase db = databaseManager.getWritableDatabase(); + writeStatement = db.compileStatement("INSERT OR REPLACE INTO " + NAME + + " (" + Fields.ACCOUNT + ", " + Fields.ROOM + + ", " + Fields.NICKNAME + ", " + + Fields.PASSWORD + ", " + Fields.NEED_JOIN + + ") VALUES (?, ?, ?, ?, ?);"); + } + writeStatement.bindString(1, account); + writeStatement.bindString(2, room); + writeStatement.bindString(3, nickname); + writeStatement.bindString(4, password); + writeStatement.bindLong(5, join ? 1 : 0); + writeStatement.execute(); + } + } + + /** + * Removes room. + */ + void remove(String account, String room) { + SQLiteDatabase db = databaseManager.getWritableDatabase(); + db.delete(NAME, Fields.ACCOUNT + " = ? AND " + Fields.ROOM + " = ?", new String[]{account, room}); + } + + @Override + protected String getTableName() { + return NAME; + } + + @Override + protected String[] getProjection() { + return PROJECTION; + } + + @Override + protected String getListOrder() { + return Fields.NEED_JOIN; + } + + static long getId(Cursor cursor) { + return cursor.getLong(cursor.getColumnIndex(Fields._ID)); + } + + static String getRoom(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.ROOM)); + } + + static String getNickname(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.NICKNAME)); + } + + static String getPassword(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.PASSWORD)); + } + + static boolean needJoin(Cursor cursor) { + return cursor.getLong(cursor.getColumnIndex(Fields.NEED_JOIN)) != 0; + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/data/extension/otr/OTRManager.java b/app/src/main/java/com/xabber/android/data/extension/otr/OTRManager.java index 1ede20a952..ac8dbabf75 100644 --- a/app/src/main/java/com/xabber/android/data/extension/otr/OTRManager.java +++ b/app/src/main/java/com/xabber/android/data/extension/otr/OTRManager.java @@ -1,43 +1,23 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data.extension.otr; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; -import java.security.PublicKey; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; - -import net.java.otr4j.OtrEngineHost; -import net.java.otr4j.OtrEngineListener; -import net.java.otr4j.OtrException; -import net.java.otr4j.OtrPolicy; -import net.java.otr4j.OtrPolicyImpl; -import net.java.otr4j.crypto.OtrCryptoEngineImpl; -import net.java.otr4j.crypto.OtrCryptoException; -import net.java.otr4j.io.SerializationUtils; -import net.java.otr4j.session.Session; -import net.java.otr4j.session.SessionID; -import net.java.otr4j.session.SessionImpl; -import net.java.otr4j.session.SessionStatus; import android.database.Cursor; +import com.xabber.android.BuildConfig; +import com.xabber.android.R; import com.xabber.android.data.Application; import com.xabber.android.data.LogManager; import com.xabber.android.data.NetworkException; @@ -61,655 +41,632 @@ import com.xabber.android.data.notification.EntityNotificationProvider; import com.xabber.android.data.notification.NotificationManager; import com.xabber.android.data.roster.RosterManager; -import com.xabber.androiddev.R; import com.xabber.xmpp.archive.OtrMode; import com.xabber.xmpp.archive.SaveMode; +import net.java.otr4j.OtrEngineHost; +import net.java.otr4j.OtrEngineListener; +import net.java.otr4j.OtrException; +import net.java.otr4j.OtrPolicy; +import net.java.otr4j.crypto.OtrCryptoEngine; +import net.java.otr4j.crypto.OtrCryptoException; +import net.java.otr4j.io.SerializationUtils; +import net.java.otr4j.session.InstanceTag; +import net.java.otr4j.session.Session; +import net.java.otr4j.session.SessionID; +import net.java.otr4j.session.SessionStatus; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; + /** * Manage off-the-record encryption. - * + *

* http://www.cypherpunks.ca/otr/ - * + * * @author alexander.ivanov - * */ public class OTRManager implements OtrEngineHost, OtrEngineListener, - OnLoadListener, OnAccountAddedListener, OnAccountRemovedListener, - OnCloseListener { - - private static Map POLICIES; - - static { - POLICIES = new HashMap(); - POLICIES.put(SecurityOtrMode.disabled, new OtrPolicyImpl( - OtrPolicy.NEVER)); - POLICIES.put(SecurityOtrMode.manual, new OtrPolicyImpl( - OtrPolicy.OTRL_POLICY_MANUAL & ~OtrPolicy.ALLOW_V1)); - POLICIES.put(SecurityOtrMode.auto, new OtrPolicyImpl( - OtrPolicy.OPPORTUNISTIC & ~OtrPolicy.ALLOW_V1)); - POLICIES.put(SecurityOtrMode.required, new OtrPolicyImpl( - OtrPolicy.OTRL_POLICY_ALWAYS & ~OtrPolicy.ALLOW_V1)); - } - - private final EntityNotificationProvider smRequestProvider; - - private final EntityNotificationProvider smProgressProvider; - - private final static OTRManager instance; - - static { - instance = new OTRManager(); - Application.getInstance().addManager(instance); - } - - public static OTRManager getInstance() { - return instance; - } - - /** - * Accepted fingerprints for user in account. - */ - private final NestedNestedMaps fingerprints; - - /** - * Fingerprint of encrypted or encrypted and verified session for user in - * account. - */ - private final NestedMap actives; - - /** - * Finished entity's sessions for users in accounts. - */ - private final NestedMap finished; - - /** - * Used OTR sessions for users in accounts. - */ - private final NestedMap sessions; - - /** - * Service for keypair generation. - */ - private final ExecutorService keyPairGenerator; - - private OTRManager() { - smRequestProvider = new EntityNotificationProvider( - R.drawable.ic_stat_request); - smProgressProvider = new EntityNotificationProvider( - R.drawable.ic_stat_play); - smProgressProvider.setCanClearNotifications(false); - fingerprints = new NestedNestedMaps(); - actives = new NestedMap(); - finished = new NestedMap(); - sessions = new NestedMap(); - keyPairGenerator = Executors - .newSingleThreadExecutor(new ThreadFactory() { - @Override - public Thread newThread(Runnable runnable) { - Thread thread = new Thread(runnable, - "Key pair generator service"); - thread.setPriority(Thread.MIN_PRIORITY); - thread.setDaemon(true); - return thread; - } - }); - } - - @Override - public void onLoad() { - final NestedNestedMaps fingerprints = new NestedNestedMaps(); - Cursor cursor = OTRTable.getInstance().list(); - try { - if (cursor.moveToFirst()) { - do { - String account = OTRTable.getAccount(cursor); - String user = OTRTable.getUser(cursor); - fingerprints.put(account, user, - OTRTable.getFingerprint(cursor), - OTRTable.isVerified(cursor)); - } while (cursor.moveToNext()); - } - } finally { - cursor.close(); - } - Application.getInstance().runOnUiThread(new Runnable() { - @Override - public void run() { - onLoaded(fingerprints); - } - }); - } - - private void onLoaded(NestedNestedMaps fingerprints) { - this.fingerprints.addAll(fingerprints); - NotificationManager.getInstance().registerNotificationProvider( - smRequestProvider); - NotificationManager.getInstance().registerNotificationProvider( - smProgressProvider); - } - - private Session getOrCreateSession(String account, String user) { - Session session = sessions.get(account, user); - if (session != null) - return session; - AccountItem accountItem = AccountManager.getInstance().getAccount( - account); - session = new SessionImpl(new SessionID(account, user, - accountItem == null ? "" : accountItem.getConnectionSettings() - .getProtocol().toString()), this); - session.addOtrEngineListener(this); - sessions.put(account, user, session); - return session; - } - - public void startSession(String account, String user) - throws NetworkException { - try { - getOrCreateSession(account, user).startSession(); - } catch (OtrException e) { - throw new NetworkException(R.string.OTR_ERROR, e); - } - } - - public void refreshSession(String account, String user) - throws NetworkException { - try { - getOrCreateSession(account, user).refreshSession(); - } catch (OtrException e) { - throw new NetworkException(R.string.OTR_ERROR, e); - } - } - - public void endSession(String account, String user) throws NetworkException { - try { - getOrCreateSession(account, user).endSession(); - } catch (OtrException e) { - throw new NetworkException(R.string.OTR_ERROR, e); - } - AbstractChat abstractChat = MessageManager.getInstance().getChat( - account, user); - MessageArchiveManager.getInstance().setSaveMode(account, user, - abstractChat.getThreadId(), SaveMode.body); - SSNManager.getInstance().setSessionOtrMode(account, user, - abstractChat.getThreadId(), OtrMode.concede); - } - - private void injectMessage(String account, String user, String msg) - throws OtrException { - AbstractChat abstractChat = MessageManager.getInstance().getChat( - account, user); - try { - MessageArchiveManager.getInstance().setSaveMode(account, user, - abstractChat.getThreadId(), SaveMode.fls); - } catch (NetworkException e) { - throw new OtrException(e); - } - SSNManager.getInstance().setSessionOtrMode(account, user, - abstractChat.getThreadId(), OtrMode.prefer); - try { - ConnectionManager.getInstance().sendPacket( - abstractChat.getAccount(), - abstractChat.createMessagePacket(msg)); - } catch (NetworkException e) { - throw new OtrException(e); - } - } - - @Override - public void injectMessage(SessionID sessionID, String msg) - throws OtrException { - injectMessage(sessionID.getAccountID(), sessionID.getUserID(), msg); - } - - /** - * Creates new action in specified chat. - * - * @param account - * @param user - * @param text - * @param action - */ - private void newAction(String account, String user, String text, - ChatAction action) { - MessageManager.getInstance().getChat(account, user) - .newAction(null, text, action); - } - - @Override - public void unreadableMessageReceived(SessionID sessionID) - throws OtrException { - newAction(sessionID.getAccountID(), sessionID.getUserID(), null, - ChatAction.otr_unreadable); - } - - @Override - public String getReplyForUnreadableMessage() { - return Application.getInstance().getString( - R.string.otr_unreadable_message); - } - - @Override - public void unencryptedMessageReceived(SessionID sessionID, String msg) - throws OtrException { - throw new OtrException(new OTRUnencryptedException(msg)); - } - - @Override - public void showError(SessionID sessionID, String error) - throws OtrException { - newAction(sessionID.getAccountID(), sessionID.getUserID(), error, - ChatAction.otr_error); - } - - @Override - public void smpError(SessionID sessionID, int tlvType, boolean cheated) - throws OtrException { - newAction(sessionID.getAccountID(), sessionID.getUserID(), null, - cheated ? ChatAction.otr_smp_cheated - : ChatAction.otr_smp_failed); - if (cheated) - removeSMProgress(sessionID.getAccountID(), sessionID.getUserID()); - } - - @Override - public void smpAborted(SessionID sessionID) throws OtrException { - removeSMRequest(sessionID.getAccountID(), sessionID.getUserID()); - removeSMProgress(sessionID.getAccountID(), sessionID.getUserID()); - } - - @Override - public void finishedSessionMessage(SessionID sessionID) throws OtrException { - newAction(sessionID.getAccountID(), sessionID.getUserID(), null, - ChatAction.otr_finished_session); - throw new OtrException( - new IllegalStateException( - "Prevent from null to be returned. Just process it as regular exception.")); - } - - @Override - public void requireEncryptedMessage(SessionID sessionID, String msgText) - throws OtrException { - throw new OtrException( - new IllegalStateException( - "Prevent from null to be returned. Just process it as regular exception.")); - } - - @Override - public OtrPolicy getSessionPolicy(SessionID sessionID) { - return POLICIES.get(SettingsManager.securityOtrMode()); - } - - private KeyPair getLocalKeyPair(String account) throws OtrException { - KeyPair keyPair = AccountManager.getInstance().getAccount(account) - .getKeyPair(); - if (keyPair == null) - throw new OtrException(new IllegalStateException( - "KeyPair is not ready, yet.")); - return keyPair; - } - - @Override - public KeyPair getLocalKeyPair(SessionID sessionID) throws OtrException { - return getLocalKeyPair(sessionID.getAccountID()); - } - - @Override - public void sessionStatusChanged(SessionID sessionID) { - removeSMRequest(sessionID.getAccountID(), sessionID.getUserID()); - removeSMProgress(sessionID.getAccountID(), sessionID.getUserID()); - Session session = sessions.get(sessionID.getAccountID(), - sessionID.getUserID()); - SessionStatus sStatus = session.getSessionStatus(); - if (sStatus == SessionStatus.ENCRYPTED) { - finished.remove(sessionID.getAccountID(), sessionID.getUserID()); - PublicKey remotePublicKey = session.getRemotePublicKey(); - String value; - try { - value = new OtrCryptoEngineImpl() - .getFingerprint(remotePublicKey); - } catch (OtrCryptoException e) { - LogManager.exception(this, e); - value = null; - } - if (value != null) { - actives.put(sessionID.getAccountID(), sessionID.getUserID(), - value); - if (fingerprints.get(sessionID.getAccountID(), - sessionID.getUserID(), value) == null) { - fingerprints.put(sessionID.getAccountID(), - sessionID.getUserID(), value, false); - requestToWrite(sessionID.getAccountID(), - sessionID.getUserID(), value, false); - } - } - newAction( - sessionID.getAccountID(), - sessionID.getUserID(), - null, - isVerified(sessionID.getAccountID(), sessionID.getUserID()) ? ChatAction.otr_verified - : ChatAction.otr_encryption); - MessageManager.getInstance() - .getChat(sessionID.getAccountID(), sessionID.getUserID()) - .sendMessages(); - } else if (sStatus == SessionStatus.PLAINTEXT) { - actives.remove(sessionID.getAccountID(), sessionID.getUserID()); - finished.remove(sessionID.getAccountID(), sessionID.getUserID()); - try { - session.endSession(); - } catch (OtrException e) { - LogManager.exception(this, e); - } - newAction(sessionID.getAccountID(), sessionID.getUserID(), null, - ChatAction.otr_plain); - } else if (sStatus == SessionStatus.FINISHED) { - actives.remove(sessionID.getAccountID(), sessionID.getUserID()); - finished.put(sessionID.getAccountID(), sessionID.getUserID(), true); - newAction(sessionID.getAccountID(), sessionID.getUserID(), null, - ChatAction.otr_finish); - } else - throw new IllegalStateException(); - RosterManager.getInstance().onContactChanged(sessionID.getAccountID(), - sessionID.getUserID()); - } - - @Override - public void askForSecret(SessionID sessionID, String question) { - smRequestProvider.add( - new SMRequest(sessionID.getAccountID(), sessionID.getUserID(), - question), true); - } - - /** - * Transform outgoing message before sending. - * - * @param account - * @param user - * @param content - * @return - * @throws OtrException - */ - public String transformSending(String account, String user, String content) - throws OtrException { - return getOrCreateSession(account, user) - .transformSending(content, null); - } - - /** - * Transform incoming message after receiving. - * - * @param account - * @param user - * @param content - * @return - * @throws OtrException - */ - public String transformReceiving(String account, String user, String content) - throws OtrException { - Session session = getOrCreateSession(account, user); - try { - return session.transformReceiving(content); - } catch (UnsupportedOperationException e) { - throw new OtrException(e); - } - } - - public SecurityLevel getSecurityLevel(String account, String user) { - if (actives.get(account, user) == null) { - if (finished.get(account, user) == null) - return SecurityLevel.plain; - else - return SecurityLevel.finished; - } else { - if (isVerified(account, user)) - return SecurityLevel.verified; - else - return SecurityLevel.encrypted; - } - } - - public boolean isVerified(String account, String user) { - String active = actives.get(account, user); - if (active == null) - return false; - Boolean value = fingerprints.get(account, user, active); - return value != null && value; - } - - private void setVerifyWithoutNotification(String account, String user, - String fingerprint, boolean value) { - fingerprints.put(account, user, fingerprint, value); - requestToWrite(account, user, fingerprint, value); - } - - /** - * Set whether fingerprint was verified. Add action to the chat history. - * - * @param account - * @param user - * @param fingerprint - * @param value - */ - public void setVerify(String account, String user, String fingerprint, - boolean value) { - setVerifyWithoutNotification(account, user, fingerprint, value); - if (value) - newAction(account, user, null, ChatAction.otr_smp_verified); - else if (actives.get(account, user) != null) - newAction(account, user, null, ChatAction.otr_encryption); - } - - private void setVerify(SessionID sessionID, boolean value) { - String active = actives.get(sessionID.getAccountID(), - sessionID.getUserID()); - if (active == null) { - LogManager.exception(this, new IllegalStateException( - "There is no active fingerprint")); - return; - } - setVerifyWithoutNotification(sessionID.getAccountID(), - sessionID.getUserID(), active, value); - newAction(sessionID.getAccountID(), sessionID.getUserID(), null, - value ? ChatAction.otr_smp_verified - : ChatAction.otr_smp_unverified); - RosterManager.getInstance().onContactChanged(sessionID.getAccountID(), - sessionID.getUserID()); - } - - @Override - public void verify(SessionID sessionID, boolean approved) { - if (approved) - setVerify(sessionID, true); - else if (isVerified(sessionID.getAccountID(), sessionID.getUserID())) - newAction(sessionID.getAccountID(), sessionID.getUserID(), null, - ChatAction.otr_smp_not_approved); - removeSMProgress(sessionID.getAccountID(), sessionID.getUserID()); - } - - @Override - public void unverify(SessionID sessionID) { - setVerify(sessionID, false); - removeSMProgress(sessionID.getAccountID(), sessionID.getUserID()); - } - - public String getRemoteFingerprint(String account, String user) { - return actives.get(account, user); - } - - public String getLocalFingerprint(String account) { - try { - return new OtrCryptoEngineImpl().getFingerprint(getLocalKeyPair( - account).getPublic()); - } catch (OtrCryptoException e) { - LogManager.exception(this, e); - } catch (OtrException e) { - LogManager.exception(this, e); - } - return null; - } - - @Override - public byte[] getLocalFingerprintRaw(SessionID sessionID) { - return SerializationUtils - .hexStringToByteArray(getLocalFingerprint(sessionID - .getAccountID())); - } - - @Override - public String getFallbackMessage() { - return Application.getInstance().getString(R.string.otr_request); - } - - /** - * Respond using SM protocol. - * - * @param account - * @param user - * @param question - * @param secret - * @throws NetworkException - */ - public void respondSmp(String account, String user, String question, - String secret) throws NetworkException { - removeSMRequest(account, user); - addSMProgress(account, user); - try { - getOrCreateSession(account, user).respondSmp(question, secret); - } catch (OtrException e) { - throw new NetworkException(R.string.OTR_ERROR, e); - } - } - - /** - * Initiate request using SM protocol. - * - * @param account - * @param user - * @param question - * @param secret - * @throws NetworkException - */ - public void initSmp(String account, String user, String question, - String secret) throws NetworkException { - removeSMRequest(account, user); - addSMProgress(account, user); - try { - getOrCreateSession(account, user).initSmp(question, secret); - } catch (OtrException e) { - throw new NetworkException(R.string.OTR_ERROR, e); - } - } - - /** - * Abort SM negotiation. - * - * @param account - * @param user - * @throws NetworkException - */ - public void abortSmp(String account, String user) throws NetworkException { - removeSMRequest(account, user); - removeSMProgress(account, user); - try { - getOrCreateSession(account, user).abortSmp(); - } catch (OtrException e) { - throw new NetworkException(R.string.OTR_ERROR, e); - } - } - - private void removeSMRequest(String account, String user) { - smRequestProvider.remove(account, user); - } - - private void addSMProgress(String account, String user) { - smProgressProvider.add(new SMProgress(account, user), false); - } - - private void removeSMProgress(String account, String user) { - smProgressProvider.remove(account, user); - } - - @Override - public void onAccountAdded(final AccountItem accountItem) { - if (accountItem.getKeyPair() != null) - return; - keyPairGenerator.execute(new Runnable() { - @Override - public void run() { - LogManager.i(this, "KeyPair generation started for " - + accountItem.getAccount()); - final KeyPair keyPair; - try { - keyPair = KeyPairGenerator.getInstance("DSA").genKeyPair(); - } catch (final NoSuchAlgorithmException e) { - Application.getInstance().runOnUiThread(new Runnable() { - @Override - public void run() { - throw new RuntimeException(e); - } - }); - return; - } - Application.getInstance().runOnUiThread(new Runnable() { - @Override - public void run() { - LogManager.i(this, "KeyPair generation finished for " - + accountItem.getAccount()); - if (AccountManager.getInstance().getAccount( - accountItem.getAccount()) != null) - AccountManager.getInstance().setKeyPair( - accountItem.getAccount(), keyPair); - } - }); - } - }); - } - - @Override - public void onAccountRemoved(AccountItem accountItem) { - fingerprints.clear(accountItem.getAccount()); - actives.clear(accountItem.getAccount()); - finished.clear(accountItem.getAccount()); - sessions.clear(accountItem.getAccount()); - } - - /** - * Save chat specific otr settings. - * - * @param account - * @param user - * @param fingerprint - * @param verified - */ - private void requestToWrite(final String account, final String user, - final String fingerprint, final boolean verified) { - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - OTRTable.getInstance().write(account, user, fingerprint, - verified); - } - }); - } - - private void endAllSessions() { - NestedMap entities = new NestedMap(); - entities.addAll(actives); - for (Entry entry : entities) - try { - endSession(entry.getFirst(), entry.getSecond()); - } catch (NetworkException e) { - LogManager.exception(this, e); - } - } - - @Override - public void onClose() { - endAllSessions(); - } - - public void onSettingsChanged() { - if (SettingsManager.securityOtrMode() == SecurityOtrMode.disabled) - endAllSessions(); - } - + OnLoadListener, OnAccountAddedListener, OnAccountRemovedListener, OnCloseListener { + + private final static OTRManager instance; + private static Map POLICIES; + + static { + POLICIES = new HashMap<>(); + POLICIES.put(SecurityOtrMode.disabled, new OtrPolicy(OtrPolicy.NEVER)); + POLICIES.put(SecurityOtrMode.manual, new OtrPolicy(OtrPolicy.OTRL_POLICY_MANUAL & ~OtrPolicy.ALLOW_V1)); + POLICIES.put(SecurityOtrMode.auto, new OtrPolicy(OtrPolicy.OPPORTUNISTIC & ~OtrPolicy.ALLOW_V1)); + POLICIES.put(SecurityOtrMode.required, new OtrPolicy(OtrPolicy.OTRL_POLICY_ALWAYS & ~OtrPolicy.ALLOW_V1)); + } + + static { + instance = new OTRManager(); + Application.getInstance().addManager(instance); + } + + private final EntityNotificationProvider smRequestProvider; + private final EntityNotificationProvider smProgressProvider; + /** + * Accepted fingerprints for user in account. + */ + private final NestedNestedMaps fingerprints; + /** + * Fingerprint of encrypted or encrypted and verified session for user in account. + */ + private final NestedMap actives; + /** + * Finished entity's sessions for users in accounts. + */ + private final NestedMap finished; + /** + * Used OTR sessions for users in accounts. + */ + private final NestedMap sessions; + /** + * Service for keypair generation. + */ + private final ExecutorService keyPairGenerator; + + private OTRManager() { + smRequestProvider = new EntityNotificationProvider<>(R.drawable.ic_stat_help); + smProgressProvider = new EntityNotificationProvider<>(R.drawable.ic_stat_play_circle_fill); + smProgressProvider.setCanClearNotifications(false); + fingerprints = new NestedNestedMaps<>(); + actives = new NestedMap<>(); + finished = new NestedMap<>(); + sessions = new NestedMap<>(); + keyPairGenerator = Executors.newSingleThreadExecutor(new ThreadFactory() { + @Override + public Thread newThread(Runnable runnable) { + Thread thread = new Thread(runnable, "Key pair generator service"); + thread.setPriority(Thread.MIN_PRIORITY); + thread.setDaemon(true); + return thread; + } + }); + } + + public static OTRManager getInstance() { + return instance; + } + + @Override + public void onLoad() { + final NestedNestedMaps fingerprints = new NestedNestedMaps<>(); + Cursor cursor = OTRTable.getInstance().list(); + try { + if (cursor.moveToFirst()) { + do { + String account = OTRTable.getAccount(cursor); + String user = OTRTable.getUser(cursor); + fingerprints.put(account, user, + OTRTable.getFingerprint(cursor), + OTRTable.isVerified(cursor)); + } while (cursor.moveToNext()); + } + } finally { + cursor.close(); + } + Application.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + onLoaded(fingerprints); + } + }); + } + + private void onLoaded(NestedNestedMaps fingerprints) { + this.fingerprints.addAll(fingerprints); + NotificationManager.getInstance().registerNotificationProvider(smRequestProvider); + NotificationManager.getInstance().registerNotificationProvider(smProgressProvider); + } + + public void startSession(String account, String user) throws NetworkException { + LogManager.i(this, "Starting session for " + user); + try { + getOrCreateSession(account, user).startSession(); + } catch (OtrException e) { + throw new NetworkException(R.string.OTR_ERROR, e); + } + LogManager.i(this, "Started session for " + user); + } + + public void refreshSession(String account, String user) throws NetworkException { + LogManager.i(this, "Refreshing session for " + user); + try { + getOrCreateSession(account, user).refreshSession(); + } catch (OtrException e) { + throw new NetworkException(R.string.OTR_ERROR, e); + } + LogManager.i(this, "Refreshed session for " + user); + } + + public void endSession(String account, String user) throws NetworkException { + LogManager.i(this, "Ending session for " + user); + try { + getOrCreateSession(account, user).endSession(); + } catch (OtrException e) { + throw new NetworkException(R.string.OTR_ERROR, e); + } + AbstractChat abstractChat = MessageManager.getInstance().getChat(account, user); + MessageArchiveManager.getInstance().setSaveMode(account, user, abstractChat.getThreadId(), SaveMode.body); + SSNManager.getInstance().setSessionOtrMode(account, user, abstractChat.getThreadId(), OtrMode.concede); + LogManager.i(this, "Ended session for " + user); + } + + private Session getOrCreateSession(String account, String user) { + Session session = sessions.get(account, user); + if (session != null) { + LogManager.i(this, "Found session with id " + session.getSessionID() + " with status " + session.getSessionStatus() + " for user " + user); + return session; + } + + LogManager.i(this, "Creating new session for " + user); + + AccountItem accountItem = AccountManager.getInstance().getAccount(account); + session = new Session(new SessionID(account, user, + accountItem == null ? "" : accountItem.getConnectionSettings().getProtocol().toString()), this); + session.addOtrEngineListener(this); + sessions.put(account, user, session); + return session; + } + + @Override + public void injectMessage(SessionID sessionID, String msg) throws OtrException { + injectMessage(sessionID.getAccountID(), sessionID.getUserID(), msg); + } + + private void injectMessage(String account, String user, String msg) throws OtrException { + LogManager.i(this, "injectMessage. user: " + user + " message: " + msg); + AbstractChat abstractChat = MessageManager.getInstance().getChat(account, user); + try { + MessageArchiveManager.getInstance().setSaveMode(account, user, abstractChat.getThreadId(), SaveMode.fls); + } catch (NetworkException e) { + throw new OtrException(e); + } + SSNManager.getInstance().setSessionOtrMode(account, user, abstractChat.getThreadId(), OtrMode.prefer); + try { + ConnectionManager.getInstance() + .sendPacket(abstractChat.getAccount(), abstractChat.createMessagePacket(msg)); + } catch (NetworkException e) { + throw new OtrException(e); + } + } + + @Override + public void unreadableMessageReceived(SessionID sessionID) throws OtrException { + LogManager.i(this, "unreadableMessageReceived"); + newAction(sessionID.getAccountID(), sessionID.getUserID(), null, ChatAction.otr_unreadable); + } + + /** + * Creates new action in specified chat. + */ + private void newAction(String account, String user, String text, ChatAction action) { + LogManager.i(this, "newAction. text: " + text + " action " + action); + MessageManager.getInstance().getChat(account, user).newAction(null, text, action); + } + + @Override + public String getReplyForUnreadableMessage(SessionID sessionID) { + return Application.getInstance().getString(R.string.otr_unreadable_message); + } + + @Override + public void unencryptedMessageReceived(SessionID sessionID, String msg) throws OtrException { + LogManager.i(this, "unencrypted Message Received. " + msg); + throw new OtrException(new OTRUnencryptedException(msg)); + } + + @Override + public void showError(SessionID sessionID, String error) throws OtrException { + LogManager.i(this, "ShowError: " + error); + newAction(sessionID.getAccountID(), sessionID.getUserID(), error, ChatAction.otr_error); + } + + @Override + public void smpError(SessionID sessionID, int tlvType, boolean cheated) throws OtrException { + newAction(sessionID.getAccountID(), sessionID.getUserID(), null, + cheated ? ChatAction.otr_smp_cheated : ChatAction.otr_smp_failed); + if (cheated) { + removeSMProgress(sessionID.getAccountID(), sessionID.getUserID()); + } + } + + @Override + public void smpAborted(SessionID sessionID) throws OtrException { + removeSMRequest(sessionID.getAccountID(), sessionID.getUserID()); + removeSMProgress(sessionID.getAccountID(), sessionID.getUserID()); + } + + @Override + public void finishedSessionMessage(SessionID sessionID, String msgText) throws OtrException { + newAction(sessionID.getAccountID(), sessionID.getUserID(), null, ChatAction.otr_finished_session); + throw new OtrException(new IllegalStateException( + "Prevent from null to be returned. Just process it as regular exception.")); + } + + @Override + public void requireEncryptedMessage(SessionID sessionID, String msgText) throws OtrException { + throw new OtrException(new IllegalStateException( + "Prevent from null to be returned. Just process it as regular exception.")); + } + + @Override + public OtrPolicy getSessionPolicy(SessionID sessionID) { + return POLICIES.get(SettingsManager.securityOtrMode()); + } + + private KeyPair getLocalKeyPair(String account) throws OtrException { + KeyPair keyPair = AccountManager.getInstance().getAccount(account).getKeyPair(); + if (keyPair == null) { + throw new OtrException(new IllegalStateException("KeyPair is not ready, yet.")); + } + return keyPair; + } + + @Override + public KeyPair getLocalKeyPair(SessionID sessionID) throws OtrException { + return getLocalKeyPair(sessionID.getAccountID()); + } + + @Override + public void sessionStatusChanged(SessionID sessionID) { + removeSMRequest(sessionID.getAccountID(), sessionID.getUserID()); + removeSMProgress(sessionID.getAccountID(), sessionID.getUserID()); + Session session = sessions.get(sessionID.getAccountID(), sessionID.getUserID()); + SessionStatus sStatus = session.getSessionStatus(); + + LogManager.i(this, "session status changed " + sessionID.getUserID() + " status: " + sStatus); + + if (sStatus == SessionStatus.ENCRYPTED) { + finished.remove(sessionID.getAccountID(), sessionID.getUserID()); + PublicKey remotePublicKey = session.getRemotePublicKey(); + String value; + try { + value = OtrCryptoEngine.getFingerprint(remotePublicKey); + } catch (OtrCryptoException e) { + LogManager.exception(this, e); + value = null; + } + if (value != null) { + actives.put(sessionID.getAccountID(), sessionID.getUserID(), value); + if (fingerprints.get(sessionID.getAccountID(), sessionID.getUserID(), value) == null) { + fingerprints.put(sessionID.getAccountID(), sessionID.getUserID(), value, false); + requestToWrite(sessionID.getAccountID(), sessionID.getUserID(), value, false); + } + } + newAction(sessionID.getAccountID(), sessionID.getUserID(), null, isVerified(sessionID.getAccountID(), + sessionID.getUserID()) ? ChatAction.otr_verified : ChatAction.otr_encryption); + MessageManager.getInstance() + .getChat(sessionID.getAccountID(), sessionID.getUserID()).sendMessages(); + } else if (sStatus == SessionStatus.PLAINTEXT) { + actives.remove(sessionID.getAccountID(), sessionID.getUserID()); + sessions.remove(sessionID.getAccountID(), sessionID.getUserID()); + finished.remove(sessionID.getAccountID(), sessionID.getUserID()); + try { + session.endSession(); + } catch (OtrException e) { + LogManager.exception(this, e); + } + newAction(sessionID.getAccountID(), sessionID.getUserID(), null, ChatAction.otr_plain); + } else if (sStatus == SessionStatus.FINISHED) { + actives.remove(sessionID.getAccountID(), sessionID.getUserID()); + sessions.remove(sessionID.getAccountID(), sessionID.getUserID()); + finished.put(sessionID.getAccountID(), sessionID.getUserID(), true); + newAction(sessionID.getAccountID(), sessionID.getUserID(), null, ChatAction.otr_finish); + } else { + throw new IllegalStateException(); + } + RosterManager.getInstance().onContactChanged(sessionID.getAccountID(), sessionID.getUserID()); + } + + @Override + public void askForSecret(SessionID sessionID, InstanceTag receiverTag, String question) { + smRequestProvider.add(new SMRequest(sessionID.getAccountID(), sessionID.getUserID(), question), true); + } + + /** + * Transform outgoing message before sending. + */ + public String transformSending(String account, String user, String content) throws OtrException { + LogManager.i(this, "transform outgoing message... " + user); + String parts[] = getOrCreateSession(account, user).transformSending(content, null); + if (BuildConfig.DEBUG && parts.length != 1) { + throw new RuntimeException( + "We do not use fragmentation, so there must be only one otr fragment."); + } + return parts[0]; + } + + /** + * Transform incoming message after receiving. + */ + public String transformReceiving(String account, String user, String content) throws OtrException { + LogManager.i(this, "transform incoming message... " + content); + Session session = getOrCreateSession(account, user); + try { + String s = session.transformReceiving(content); + LogManager.i(this, "transformed incoming message: " + s + " session status: " + session.getSessionStatus()); + return s; + } catch (UnsupportedOperationException e) { + throw new OtrException(e); + } + } + + public SecurityLevel getSecurityLevel(String account, String user) { + if (actives.get(account, user) == null) { + if (finished.get(account, user) == null) { + return SecurityLevel.plain; + } else { + return SecurityLevel.finished; + } + } else { + if (isVerified(account, user)) { + return SecurityLevel.verified; + } else { + return SecurityLevel.encrypted; + } + } + } + + public boolean isVerified(String account, String user) { + String active = actives.get(account, user); + if (active == null) { + return false; + } + Boolean value = fingerprints.get(account, user, active); + return value != null && value; + } + + private void setVerifyWithoutNotification(String account, String user, String fingerprint, boolean value) { + fingerprints.put(account, user, fingerprint, value); + requestToWrite(account, user, fingerprint, value); + } + + /** + * Set whether fingerprint was verified. Add action to the chat history. + */ + public void setVerify(String account, String user, String fingerprint, boolean value) { + setVerifyWithoutNotification(account, user, fingerprint, value); + if (value) { + newAction(account, user, null, ChatAction.otr_smp_verified); + } else if (actives.get(account, user) != null) { + newAction(account, user, null, ChatAction.otr_encryption); + } + } + + private void setVerify(SessionID sessionID, boolean value) { + String active = actives.get(sessionID.getAccountID(), sessionID.getUserID()); + if (active == null) { + LogManager.exception(this, new IllegalStateException("There is no active fingerprint")); + return; + } + setVerifyWithoutNotification(sessionID.getAccountID(), sessionID.getUserID(), active, value); + newAction(sessionID.getAccountID(), sessionID.getUserID(), null, + value ? ChatAction.otr_smp_verified : ChatAction.otr_smp_unverified); + RosterManager.getInstance().onContactChanged(sessionID.getAccountID(), sessionID.getUserID()); + } + + @Override + public void verify(SessionID sessionID, String fingerprint, boolean approved) { + if (approved) { + setVerify(sessionID, true); + } else if (isVerified(sessionID.getAccountID(), sessionID.getUserID())) { + newAction(sessionID.getAccountID(), sessionID.getUserID(), null, ChatAction.otr_smp_not_approved); + } + removeSMProgress(sessionID.getAccountID(), sessionID.getUserID()); + } + + @Override + public void unverify(SessionID sessionID, String fingerprint) { + setVerify(sessionID, false); + removeSMProgress(sessionID.getAccountID(), sessionID.getUserID()); + } + + public String getRemoteFingerprint(String account, String user) { + return actives.get(account, user); + } + + public String getLocalFingerprint(String account) { + try { + return OtrCryptoEngine.getFingerprint(getLocalKeyPair(account).getPublic()); + } catch (OtrException e) { + LogManager.exception(this, e); + } + return null; + } + + @Override + public byte[] getLocalFingerprintRaw(SessionID sessionID) { + return SerializationUtils.hexStringToByteArray(getLocalFingerprint(sessionID.getAccountID())); + } + + @Override + public String getFallbackMessage(SessionID sessionID) { + return Application.getInstance().getString(R.string.otr_request); + } + + /** + * Respond using SM protocol. + */ + public void respondSmp(String account, String user, String question, String secret) throws NetworkException { + LogManager.i(this, "responding smp... " + user); + removeSMRequest(account, user); + addSMProgress(account, user); + try { + getOrCreateSession(account, user).respondSmp(question, secret); + } catch (OtrException e) { + throw new NetworkException(R.string.OTR_ERROR, e); + } + } + + /** + * Initiate request using SM protocol. + */ + public void initSmp(String account, String user, String question, String secret) throws NetworkException { + LogManager.i(this, "initializing smp... " + user); + removeSMRequest(account, user); + addSMProgress(account, user); + try { + getOrCreateSession(account, user).initSmp(question, secret); + } catch (OtrException e) { + throw new NetworkException(R.string.OTR_ERROR, e); + } + } + + /** + * Abort SM negotiation. + */ + public void abortSmp(String account, String user) throws NetworkException { + LogManager.i(this, "aborting smp... " + user); + removeSMRequest(account, user); + removeSMProgress(account, user); + try { + getOrCreateSession(account, user).abortSmp(); + } catch (OtrException e) { + throw new NetworkException(R.string.OTR_ERROR, e); + } + } + + private void removeSMRequest(String account, String user) { + smRequestProvider.remove(account, user); + } + + private void addSMProgress(String account, String user) { + smProgressProvider.add(new SMProgress(account, user), false); + } + + private void removeSMProgress(String account, String user) { + smProgressProvider.remove(account, user); + } + + @Override + public void onAccountAdded(final AccountItem accountItem) { + if (accountItem.getKeyPair() != null) { + return; + } + keyPairGenerator.execute(new Runnable() { + @Override + public void run() { + LogManager.i(this, "KeyPair generation started for " + accountItem.getAccount()); + final KeyPair keyPair; + try { + keyPair = KeyPairGenerator.getInstance("DSA").genKeyPair(); + } catch (final NoSuchAlgorithmException e) { + Application.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + throw new RuntimeException(e); + } + }); + return; + } + Application.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + LogManager.i(this, "KeyPair generation finished for " + accountItem.getAccount()); + if (AccountManager.getInstance().getAccount(accountItem.getAccount()) != null) { + AccountManager.getInstance().setKeyPair(accountItem.getAccount(), keyPair); + } + } + }); + } + }); + } + + @Override + public void onAccountRemoved(AccountItem accountItem) { + fingerprints.clear(accountItem.getAccount()); + actives.clear(accountItem.getAccount()); + finished.clear(accountItem.getAccount()); + sessions.clear(accountItem.getAccount()); + } + + /** + * Save chat specific otr settings. + */ + private void requestToWrite(final String account, final String user, + final String fingerprint, final boolean verified) { + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + OTRTable.getInstance().write(account, user, fingerprint, verified); + } + }); + } + + private void endAllSessions() { + LogManager.i(this, "End all sessions"); + NestedMap entities = new NestedMap<>(); + entities.addAll(actives); + for (Entry entry : entities) { + try { + endSession(entry.getFirst(), entry.getSecond()); + } catch (NetworkException e) { + LogManager.exception(this, e); + } + } + } + + @Override + public void onClose() { + endAllSessions(); + } + + public void onSettingsChanged() { + if (SettingsManager.securityOtrMode() == SecurityOtrMode.disabled) { + endAllSessions(); + } + } + + @Override + public int getMaxFragmentSize(SessionID sessionID) { + // we do not want fragmentation + return Integer.MAX_VALUE; + } + + @Override + public void outgoingSessionChanged(SessionID sessionID) { + LogManager.i(this, "Outgoing session change with SessionID " + sessionID); + // TODO what to in this situation? + } + + @Override + public void messageFromAnotherInstanceReceived(SessionID sessionID) { + LogManager.i(this, "Message from another instance received on SessionID " + + sessionID + ". Restarting OTR session for this user."); + newAction(sessionID.getAccountID(), sessionID.getUserID(), null, ChatAction.otr_unreadable); + } + + @Override + public void multipleInstancesDetected(SessionID sessionID) { + LogManager.i(this, "Multiple instances detected on SessionID " + sessionID); + // since this is not supported, we don't need to do anything + } + + public void onContactUnAvailable(String account, String user) { + Session session = sessions.get(account, user); + + if (session == null) { + return; + } + + if (session.getSessionStatus() == SessionStatus.ENCRYPTED) { + try { + LogManager.i(this, "onContactUnAvailable. Refresh session for " + user); + session.refreshSession(); + } catch (OtrException e) { + LogManager.exception(this, e); + } + } + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/otr/OTRTable.java b/app/src/main/java/com/xabber/android/data/extension/otr/OTRTable.java index c723fc8a70..6324943294 100644 --- a/app/src/main/java/com/xabber/android/data/extension/otr/OTRTable.java +++ b/app/src/main/java/com/xabber/android/data/extension/otr/OTRTable.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -23,121 +23,121 @@ /** * Storage with OTR finger prints and trusted level. - * + * * @author alexander.ivanov */ class OTRTable extends AbstractEntityTable { - private static final class Fields implements AbstractEntityTable.Fields { - - private Fields() { - } - - public static final String USER = "user"; - public static final String FINGERPRINT = "fingerprint"; - public static final String VERIFIED = "verified"; - - } - - private static final String NAME = "otr"; - private static final String[] PROJECTION = new String[] { Fields.ACCOUNT, - Fields.USER, Fields.FINGERPRINT, Fields.VERIFIED, }; - - private final DatabaseManager databaseManager; - private SQLiteStatement writeStatement; - private final Object writeLock; - - private final static OTRTable instance; - - static { - instance = new OTRTable(DatabaseManager.getInstance()); - DatabaseManager.getInstance().addTable(instance); - } - - public static OTRTable getInstance() { - return instance; - } - - private OTRTable(DatabaseManager databaseManager) { - this.databaseManager = databaseManager; - writeStatement = null; - writeLock = new Object(); - } - - @Override - public void create(SQLiteDatabase db) { - String sql; - sql = "CREATE TABLE " + NAME + " (" + Fields.ACCOUNT + " TEXT," - + Fields.USER + " TEXT," + Fields.FINGERPRINT + " TEXT," - + Fields.VERIFIED + " INTEGER);"; - DatabaseManager.execSQL(db, sql); - sql = "CREATE UNIQUE INDEX " + NAME + "_list ON " + NAME + " (" - + Fields.ACCOUNT + ", " + Fields.USER + ", " - + Fields.FINGERPRINT + ");"; - DatabaseManager.execSQL(db, sql); - } - - @Override - public void migrate(SQLiteDatabase db, int toVersion) { - super.migrate(db, toVersion); - String sql; - switch (toVersion) { - case 54: - sql = "CREATE TABLE otr (" + "account TEXT," + "user TEXT," - + "fingerprint TEXT," + "verified INTEGER);"; - DatabaseManager.execSQL(db, sql); - sql = "CREATE UNIQUE INDEX otr_list ON otr (account, user);"; - DatabaseManager.execSQL(db, sql); - break; - case 56: - sql = "DROP INDEX otr_list;"; - DatabaseManager.execSQL(db, sql); - sql = "CREATE UNIQUE INDEX otr_list ON otr (account, user, fingerprint);"; - DatabaseManager.execSQL(db, sql); - default: - break; - } - } - - void write(String account, String user, String fingerprint, boolean verified) { - synchronized (writeLock) { - if (writeStatement == null) { - SQLiteDatabase db = databaseManager.getWritableDatabase(); - writeStatement = db.compileStatement("INSERT OR REPLACE INTO " - + NAME + " (" + Fields.ACCOUNT + ", " + Fields.USER - + ", " + Fields.FINGERPRINT + ", " + Fields.VERIFIED - + ") VALUES (?, ?, ?, ?);"); - } - writeStatement.bindString(1, account); - writeStatement.bindString(2, user); - writeStatement.bindString(3, fingerprint); - writeStatement.bindLong(4, verified ? 1 : 0); - writeStatement.execute(); - } - } - - void remove(String account, String user) { - SQLiteDatabase db = databaseManager.getWritableDatabase(); - db.delete(NAME, Fields.ACCOUNT + " = ? AND " + Fields.USER + " = ?", - new String[] { account, user }); - } - - @Override - protected String getTableName() { - return NAME; - } - - @Override - protected String[] getProjection() { - return PROJECTION; - } - - static String getFingerprint(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.FINGERPRINT)); - } - - static boolean isVerified(Cursor cursor) { - return cursor.getLong(cursor.getColumnIndex(Fields.VERIFIED)) != 0; - } + private static final class Fields implements AbstractEntityTable.Fields { + + private Fields() { + } + + public static final String USER = "user"; + public static final String FINGERPRINT = "fingerprint"; + public static final String VERIFIED = "verified"; + + } + + private static final String NAME = "otr"; + private static final String[] PROJECTION = new String[]{Fields.ACCOUNT, + Fields.USER, Fields.FINGERPRINT, Fields.VERIFIED,}; + + private final DatabaseManager databaseManager; + private SQLiteStatement writeStatement; + private final Object writeLock; + + private final static OTRTable instance; + + static { + instance = new OTRTable(DatabaseManager.getInstance()); + DatabaseManager.getInstance().addTable(instance); + } + + public static OTRTable getInstance() { + return instance; + } + + private OTRTable(DatabaseManager databaseManager) { + this.databaseManager = databaseManager; + writeStatement = null; + writeLock = new Object(); + } + + @Override + public void create(SQLiteDatabase db) { + String sql; + sql = "CREATE TABLE " + NAME + " (" + Fields.ACCOUNT + " TEXT," + + Fields.USER + " TEXT," + Fields.FINGERPRINT + " TEXT," + + Fields.VERIFIED + " INTEGER);"; + DatabaseManager.execSQL(db, sql); + sql = "CREATE UNIQUE INDEX " + NAME + "_list ON " + NAME + " (" + + Fields.ACCOUNT + ", " + Fields.USER + ", " + + Fields.FINGERPRINT + ");"; + DatabaseManager.execSQL(db, sql); + } + + @Override + public void migrate(SQLiteDatabase db, int toVersion) { + super.migrate(db, toVersion); + String sql; + switch (toVersion) { + case 54: + sql = "CREATE TABLE otr (" + "account TEXT," + "user TEXT," + + "fingerprint TEXT," + "verified INTEGER);"; + DatabaseManager.execSQL(db, sql); + sql = "CREATE UNIQUE INDEX otr_list ON otr (account, user);"; + DatabaseManager.execSQL(db, sql); + break; + case 56: + sql = "DROP INDEX otr_list;"; + DatabaseManager.execSQL(db, sql); + sql = "CREATE UNIQUE INDEX otr_list ON otr (account, user, fingerprint);"; + DatabaseManager.execSQL(db, sql); + default: + break; + } + } + + void write(String account, String user, String fingerprint, boolean verified) { + synchronized (writeLock) { + if (writeStatement == null) { + SQLiteDatabase db = databaseManager.getWritableDatabase(); + writeStatement = db.compileStatement("INSERT OR REPLACE INTO " + + NAME + " (" + Fields.ACCOUNT + ", " + Fields.USER + + ", " + Fields.FINGERPRINT + ", " + Fields.VERIFIED + + ") VALUES (?, ?, ?, ?);"); + } + writeStatement.bindString(1, account); + writeStatement.bindString(2, user); + writeStatement.bindString(3, fingerprint); + writeStatement.bindLong(4, verified ? 1 : 0); + writeStatement.execute(); + } + } + + void remove(String account, String user) { + SQLiteDatabase db = databaseManager.getWritableDatabase(); + db.delete(NAME, Fields.ACCOUNT + " = ? AND " + Fields.USER + " = ?", + new String[]{account, user}); + } + + @Override + protected String getTableName() { + return NAME; + } + + @Override + protected String[] getProjection() { + return PROJECTION; + } + + static String getFingerprint(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.FINGERPRINT)); + } + + static boolean isVerified(Cursor cursor) { + return cursor.getLong(cursor.getColumnIndex(Fields.VERIFIED)) != 0; + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/data/extension/otr/OTRUnencryptedException.java b/app/src/main/java/com/xabber/android/data/extension/otr/OTRUnencryptedException.java index 144f3ba0e7..734c899efa 100644 --- a/app/src/main/java/com/xabber/android/data/extension/otr/OTRUnencryptedException.java +++ b/app/src/main/java/com/xabber/android/data/extension/otr/OTRUnencryptedException.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,17 +16,17 @@ public class OTRUnencryptedException extends Exception { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - private final String text; + private final String text; - public OTRUnencryptedException(String text) { - super(); - this.text = text; - } + public OTRUnencryptedException(String text) { + super(); + this.text = text; + } - public String getText() { - return text; - } + public String getText() { + return text; + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/otr/SMProgress.java b/app/src/main/java/com/xabber/android/data/extension/otr/SMProgress.java index 21a586ccf3..22a9096484 100644 --- a/app/src/main/java/com/xabber/android/data/extension/otr/SMProgress.java +++ b/app/src/main/java/com/xabber/android/data/extension/otr/SMProgress.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,35 +16,33 @@ import android.content.Intent; +import com.xabber.android.R; import com.xabber.android.data.Application; import com.xabber.android.data.entity.BaseEntity; import com.xabber.android.data.notification.EntityNotificationItem; import com.xabber.android.data.roster.RosterManager; import com.xabber.android.ui.QuestionViewer; -import com.xabber.androiddev.R; public class SMProgress extends BaseEntity implements EntityNotificationItem { - public SMProgress(String account, String user) { - super(account, user); - } - - @Override - public Intent getIntent() { - return QuestionViewer.createCanelIntent( - Application.getInstance(), account, user); - } - - @Override - public String getTitle() { - return Application.getInstance().getString(R.string.otr_verification) - + " " + RosterManager.getInstance().getName(account, user); - } - - @Override - public String getText() { - return Application.getInstance().getString( - R.string.otr_verification_in_progress); - } + public SMProgress(String account, String user) { + super(account, user); + } + + @Override + public Intent getIntent() { + return QuestionViewer.createCancelIntent(Application.getInstance(), account, user); + } + + @Override + public String getTitle() { + return Application.getInstance().getString(R.string.otr_verification) + + " " + RosterManager.getInstance().getName(account, user); + } + + @Override + public String getText() { + return Application.getInstance().getString(R.string.otr_verification_in_progress); + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/otr/SMRequest.java b/app/src/main/java/com/xabber/android/data/extension/otr/SMRequest.java index 0d709e8c59..29f0806baf 100644 --- a/app/src/main/java/com/xabber/android/data/extension/otr/SMRequest.java +++ b/app/src/main/java/com/xabber/android/data/extension/otr/SMRequest.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,37 +16,36 @@ import android.content.Intent; +import com.xabber.android.R; import com.xabber.android.data.Application; import com.xabber.android.data.entity.BaseEntity; import com.xabber.android.data.notification.EntityNotificationItem; import com.xabber.android.data.roster.RosterManager; import com.xabber.android.ui.QuestionViewer; -import com.xabber.androiddev.R; public class SMRequest extends BaseEntity implements EntityNotificationItem { - private final String question; - - public SMRequest(String account, String user, String question) { - super(account, user); - this.question = question; - } - - @Override - public Intent getIntent() { - return QuestionViewer.createIntent( - Application.getInstance(), account, user, question != null, - true, question); - } - - @Override - public String getTitle() { - return Application.getInstance().getString(R.string.otr_verification); - } - - @Override - public String getText() { - return RosterManager.getInstance().getName(account, user); - } + private final String question; + + public SMRequest(String account, String user, String question) { + super(account, user); + this.question = question; + } + + @Override + public Intent getIntent() { + return QuestionViewer.createIntent( + Application.getInstance(), account, user, question != null, true, question); + } + + @Override + public String getTitle() { + return Application.getInstance().getString(R.string.otr_verification); + } + + @Override + public String getText() { + return RosterManager.getInstance().getName(account, user); + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/otr/SecurityLevel.java b/app/src/main/java/com/xabber/android/data/extension/otr/SecurityLevel.java index 953a5bf427..89822b3028 100644 --- a/app/src/main/java/com/xabber/android/data/extension/otr/SecurityLevel.java +++ b/app/src/main/java/com/xabber/android/data/extension/otr/SecurityLevel.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,34 +16,33 @@ /** * Level of the chat security. - * + * * @author alexander.ivanov - * */ public enum SecurityLevel { - /** - * Chat without encryption. - */ - plain, + /** + * Chat without encryption. + */ + plain, - /** - * Encrypted chat but not verified certificate. - */ - encrypted, + /** + * Encrypted chat but not verified certificate. + */ + encrypted, - /** - * Encrypted chat with verified certificate. - */ - verified, + /** + * Encrypted chat with verified certificate. + */ + verified, - /** - * Session was finished by another entity. - */ - finished; + /** + * Session was finished by another entity. + */ + finished; - public int getImageLevel() { - return ordinal(); - } + public int getImageLevel() { + return ordinal(); + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/ping/PingManager.java b/app/src/main/java/com/xabber/android/data/extension/ping/PingManager.java index 7b8416f478..63fe1e4f6c 100644 --- a/app/src/main/java/com/xabber/android/data/extension/ping/PingManager.java +++ b/app/src/main/java/com/xabber/android/data/extension/ping/PingManager.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -31,52 +31,51 @@ /** * Reply on incoming ping requests. - * + * * @author alexander.ivanov - * */ public class PingManager implements OnPacketListener { - private final static PingManager instance; + private final static PingManager instance; - static { - instance = new PingManager(); - Application.getInstance().addManager(instance); + static { + instance = new PingManager(); + Application.getInstance().addManager(instance); - Connection - .addConnectionCreationListener(new ConnectionCreationListener() { - @Override - public void connectionCreated(final Connection connection) { - ServiceDiscoveryManager.getInstanceFor(connection) - .addFeature("urn:xmpp:ping"); - } - }); - } + Connection + .addConnectionCreationListener(new ConnectionCreationListener() { + @Override + public void connectionCreated(final Connection connection) { + ServiceDiscoveryManager.getInstanceFor(connection) + .addFeature("urn:xmpp:ping"); + } + }); + } - public static PingManager getInstance() { - return instance; - } + public static PingManager getInstance() { + return instance; + } - private PingManager() { - } + private PingManager() { + } - @Override - public void onPacket(ConnectionItem connection, final String bareAddress, - Packet packet) { - if (!(connection instanceof AccountItem)) - return; - final String account = ((AccountItem) connection).getAccount(); - if (!(packet instanceof Ping)) - return; - final Ping ping = (Ping) packet; - if (ping.getType() != IQ.Type.GET) - return; - try { - ConnectionManager.getInstance().sendPacket(account, - IQ.createResultIQ(ping)); - } catch (NetworkException e) { - LogManager.exception(this, e); - } - } + @Override + public void onPacket(ConnectionItem connection, final String bareAddress, + Packet packet) { + if (!(connection instanceof AccountItem)) + return; + final String account = ((AccountItem) connection).getAccount(); + if (!(packet instanceof Ping)) + return; + final Ping ping = (Ping) packet; + if (ping.getType() != IQ.Type.GET) + return; + try { + ConnectionManager.getInstance().sendPacket(account, + IQ.createResultIQ(ping)); + } catch (NetworkException e) { + LogManager.exception(this, e); + } + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/ssn/SSNManager.java b/app/src/main/java/com/xabber/android/data/extension/ssn/SSNManager.java index 44b7178fc8..d8bd20ed68 100644 --- a/app/src/main/java/com/xabber/android/data/extension/ssn/SSNManager.java +++ b/app/src/main/java/com/xabber/android/data/extension/ssn/SSNManager.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -42,261 +42,260 @@ /** * Stanza Session Negotiation. - * + *

* http://xmpp.org/extensions/xep-0155.html - * + * * @author alexander.ivanov - * */ public class SSNManager implements OnPacketListener, OnAccountRemovedListener { - /** - * Session state for the session id in account. - */ - private final NestedMap sessionStates; + /** + * Session state for the session id in account. + */ + private final NestedMap sessionStates; - /** - * OTR encryption mode for the session id in account. - */ - private final NestedMap sessionOtrs; + /** + * OTR encryption mode for the session id in account. + */ + private final NestedMap sessionOtrs; - private final static SSNManager instance; + private final static SSNManager instance; - static { - instance = new SSNManager(Application.getInstance()); - Application.getInstance().addManager(instance); - } + static { + instance = new SSNManager(Application.getInstance()); + Application.getInstance().addManager(instance); + } - public static SSNManager getInstance() { - return instance; - } + public static SSNManager getInstance() { + return instance; + } - private SSNManager(Application application) { - sessionStates = new NestedMap(); - sessionOtrs = new NestedMap(); - } + private SSNManager(Application application) { + sessionStates = new NestedMap(); + sessionOtrs = new NestedMap(); + } - @Override - public void onAccountRemoved(AccountItem accountItem) { - sessionStates.clear(accountItem.getAccount()); - sessionOtrs.clear(accountItem.getAccount()); - } + @Override + public void onAccountRemoved(AccountItem accountItem) { + sessionStates.clear(accountItem.getAccount()); + sessionOtrs.clear(accountItem.getAccount()); + } - @Override - public void onPacket(ConnectionItem connection, final String bareAddress, - Packet packet) { - String from = packet.getFrom(); - if (from == null) - return; - if (!(connection instanceof AccountItem) - || !(packet instanceof Message)) - return; - String account = ((AccountItem) connection).getAccount(); - Message message = (Message) packet; - String session = message.getThread(); - if (session == null) - return; - for (PacketExtension packetExtension : packet.getExtensions()) - if (packetExtension instanceof Feature) { - Feature feature = (Feature) packetExtension; - if (!feature.isValid()) - continue; - DataFormType type = feature.getDataFormType(); - if (type == DataFormType.form) - onFormReceived(account, from, bareAddress, session, feature); - else if (type == DataFormType.submit) - onSubmitReceived(account, from, bareAddress, session, - feature); - else if (type == DataFormType.result) - onResultReceived(account, from, bareAddress, session, - feature); - } - } + @Override + public void onPacket(ConnectionItem connection, final String bareAddress, + Packet packet) { + String from = packet.getFrom(); + if (from == null) + return; + if (!(connection instanceof AccountItem) + || !(packet instanceof Message)) + return; + String account = ((AccountItem) connection).getAccount(); + Message message = (Message) packet; + String session = message.getThread(); + if (session == null) + return; + for (PacketExtension packetExtension : packet.getExtensions()) + if (packetExtension instanceof Feature) { + Feature feature = (Feature) packetExtension; + if (!feature.isValid()) + continue; + DataFormType type = feature.getDataFormType(); + if (type == DataFormType.form) + onFormReceived(account, from, bareAddress, session, feature); + else if (type == DataFormType.submit) + onSubmitReceived(account, from, bareAddress, session, + feature); + else if (type == DataFormType.result) + onResultReceived(account, from, bareAddress, session, + feature); + } + } - private void onFormReceived(String account, String from, - String bareAddress, String session, Feature feature) { - OtrMode otrMode = getOtrMode(account, bareAddress, session); - boolean cancel = false; + private void onFormReceived(String account, String from, + String bareAddress, String session, Feature feature) { + OtrMode otrMode = getOtrMode(account, bareAddress, session); + boolean cancel = false; - Collection disclosureValues = feature - .getDisclosureOptions(); - DisclosureValue disclosureValue = DisclosureValue.never; - if (disclosureValues == null) - disclosureValue = null; - else if (!disclosureValues.contains(disclosureValue)) - cancel = true; + Collection disclosureValues = feature + .getDisclosureOptions(); + DisclosureValue disclosureValue = DisclosureValue.never; + if (disclosureValues == null) + disclosureValue = null; + else if (!disclosureValues.contains(disclosureValue)) + cancel = true; - Collection securityValues = feature.getSecurityOptions(); - SecurityValue securityValue; - if (AccountManager.getInstance().getAccount(account) - .getConnectionSettings().getTlsMode() == TLSMode.required) - securityValue = SecurityValue.c2s; - else - securityValue = SecurityValue.none; - if (securityValues == null) - securityValue = null; - else if (!securityValues.contains(securityValue)) - cancel = true; + Collection securityValues = feature.getSecurityOptions(); + SecurityValue securityValue; + if (AccountManager.getInstance().getAccount(account) + .getConnectionSettings().getTlsMode() == TLSMode.required) + securityValue = SecurityValue.c2s; + else + securityValue = SecurityValue.none; + if (securityValues == null) + securityValue = null; + else if (!securityValues.contains(securityValue)) + cancel = true; - Collection loggingValues = feature.getLoggingOptions(); - LoggingValue loggingValue; - if (loggingValues == null) - loggingValue = null; - else { - loggingValue = otrMode.selectLoggingValue(loggingValues); - if (loggingValue == null) - cancel = true; - } + Collection loggingValues = feature.getLoggingOptions(); + LoggingValue loggingValue; + if (loggingValues == null) + loggingValue = null; + else { + loggingValue = otrMode.selectLoggingValue(loggingValues); + if (loggingValue == null) + cancel = true; + } - if (cancel) { - DataForm dataForm = Feature.createDataForm(DataFormType.submit); - if (feature.getAcceptValue() != null) { - Feature.addAcceptField(dataForm, false); - sessionStates.remove(account, session); - } else { - Feature.addRenegotiateField(dataForm, false); - } - sendFeature(account, from, session, new Feature(dataForm)); - return; - } + if (cancel) { + DataForm dataForm = Feature.createDataForm(DataFormType.submit); + if (feature.getAcceptValue() != null) { + Feature.addAcceptField(dataForm, false); + sessionStates.remove(account, session); + } else { + Feature.addRenegotiateField(dataForm, false); + } + sendFeature(account, from, session, new Feature(dataForm)); + return; + } - DataForm dataForm = Feature.createDataForm(DataFormType.submit); - if (feature.getAcceptValue() != null) - Feature.addAcceptField(dataForm, true); - else - Feature.addRenegotiateField(dataForm, true); - if (disclosureValue != null) - Feature.addDisclosureField(dataForm, null, disclosureValue); - if (securityValue != null) - Feature.addSecurityField(dataForm, null, securityValue); - if (loggingValue != null) { - try { - if (loggingValue == LoggingValue.mustnot) - MessageArchiveManager.getInstance().setSaveMode(account, - from, session, SaveMode.fls); - else - MessageArchiveManager.getInstance().setSaveMode(account, - from, session, SaveMode.body); - } catch (NetworkException e) { - } - Feature.addLoggingField(dataForm, null, loggingValue); - } - sessionStates.put(account, session, SessionState.active); - sendFeature(account, from, session, new Feature(dataForm)); - } + DataForm dataForm = Feature.createDataForm(DataFormType.submit); + if (feature.getAcceptValue() != null) + Feature.addAcceptField(dataForm, true); + else + Feature.addRenegotiateField(dataForm, true); + if (disclosureValue != null) + Feature.addDisclosureField(dataForm, null, disclosureValue); + if (securityValue != null) + Feature.addSecurityField(dataForm, null, securityValue); + if (loggingValue != null) { + try { + if (loggingValue == LoggingValue.mustnot) + MessageArchiveManager.getInstance().setSaveMode(account, + from, session, SaveMode.fls); + else + MessageArchiveManager.getInstance().setSaveMode(account, + from, session, SaveMode.body); + } catch (NetworkException e) { + } + Feature.addLoggingField(dataForm, null, loggingValue); + } + sessionStates.put(account, session, SessionState.active); + sendFeature(account, from, session, new Feature(dataForm)); + } - private void onSubmitReceived(String account, String from, - String bareAddress, String session, Feature feature) { - if (feature.getTerminateValue() != null) { - onTerminateReceived(account, from, session); - return; - } - if (!isAccepted(account, from, bareAddress, session, feature)) - return; - OtrMode otrMode = getOtrMode(account, bareAddress, session); - LoggingValue loggingValue = feature.getLoggingValue(); - if (loggingValue == null || otrMode.acceptLoggingValue(loggingValue)) { - DataForm dataForm = Feature.createDataForm(DataFormType.result); - if (feature.getAcceptValue() != null) - Feature.addAcceptField(dataForm, true); - else - Feature.addRenegotiateField(dataForm, true); - sendFeature(account, from, session, new Feature(dataForm)); - sessionStates.put(account, session, SessionState.active); - } else { - DataForm dataForm = Feature.createDataForm(DataFormType.result); - if (feature.getAcceptValue() != null) { - Feature.addAcceptField(dataForm, false); - sessionStates.remove(account, session); - } else - Feature.addRenegotiateField(dataForm, false); - sendFeature(account, from, session, new Feature(dataForm)); - } - } + private void onSubmitReceived(String account, String from, + String bareAddress, String session, Feature feature) { + if (feature.getTerminateValue() != null) { + onTerminateReceived(account, from, session); + return; + } + if (!isAccepted(account, from, bareAddress, session, feature)) + return; + OtrMode otrMode = getOtrMode(account, bareAddress, session); + LoggingValue loggingValue = feature.getLoggingValue(); + if (loggingValue == null || otrMode.acceptLoggingValue(loggingValue)) { + DataForm dataForm = Feature.createDataForm(DataFormType.result); + if (feature.getAcceptValue() != null) + Feature.addAcceptField(dataForm, true); + else + Feature.addRenegotiateField(dataForm, true); + sendFeature(account, from, session, new Feature(dataForm)); + sessionStates.put(account, session, SessionState.active); + } else { + DataForm dataForm = Feature.createDataForm(DataFormType.result); + if (feature.getAcceptValue() != null) { + Feature.addAcceptField(dataForm, false); + sessionStates.remove(account, session); + } else + Feature.addRenegotiateField(dataForm, false); + sendFeature(account, from, session, new Feature(dataForm)); + } + } - private void onTerminateReceived(String account, String from, String session) { - if (sessionStates.get(account, session) == null) - return; - sessionStates.remove(account, session); - DataForm dataForm = Feature.createDataForm(DataFormType.result); - Feature.addTerminateField(dataForm); - sendFeature(account, from, session, new Feature(dataForm)); - } + private void onTerminateReceived(String account, String from, String session) { + if (sessionStates.get(account, session) == null) + return; + sessionStates.remove(account, session); + DataForm dataForm = Feature.createDataForm(DataFormType.result); + Feature.addTerminateField(dataForm); + sendFeature(account, from, session, new Feature(dataForm)); + } - private OtrMode getOtrMode(String account, String bareAddress, - String session) { - OtrMode otrMode = sessionOtrs.get(account, session); - if (otrMode != null) - return otrMode; - otrMode = MessageArchiveManager.getInstance().getOtrMode(account, - bareAddress); - if (otrMode != null) - return otrMode; - return OtrMode.concede; - } + private OtrMode getOtrMode(String account, String bareAddress, + String session) { + OtrMode otrMode = sessionOtrs.get(account, session); + if (otrMode != null) + return otrMode; + otrMode = MessageArchiveManager.getInstance().getOtrMode(account, + bareAddress); + if (otrMode != null) + return otrMode; + return OtrMode.concede; + } - private boolean isAccepted(String account, String from, String bareAddress, - String session, Feature feature) { - Boolean accept = feature.getAcceptValue(); - if (accept != null && !accept) { - sessionStates.remove(account, session); - return false; - } - Boolean renegotiate = feature.getRenegotiateValue(); - if (renegotiate != null && !renegotiate) { - if (sessionStates.get(account, session) == SessionState.renegotiation) - sessionStates.put(account, session, SessionState.active); - return false; - } - return true; - } + private boolean isAccepted(String account, String from, String bareAddress, + String session, Feature feature) { + Boolean accept = feature.getAcceptValue(); + if (accept != null && !accept) { + sessionStates.remove(account, session); + return false; + } + Boolean renegotiate = feature.getRenegotiateValue(); + if (renegotiate != null && !renegotiate) { + if (sessionStates.get(account, session) == SessionState.renegotiation) + sessionStates.put(account, session, SessionState.active); + return false; + } + return true; + } - private void onResultReceived(String account, String from, - String bareAddress, String session, Feature feature) { - isAccepted(account, from, bareAddress, session, feature); - } + private void onResultReceived(String account, String from, + String bareAddress, String session, Feature feature) { + isAccepted(account, from, bareAddress, session, feature); + } - /** - * Sets OTR mode for the session and starts negotiation / renegotiation. - * - * @param account - * @param user - * @param session - * @param otrMode - * @throws NetworkException - */ - public void setSessionOtrMode(String account, String user, String session, - OtrMode otrMode) { - if (sessionOtrs.get(account, session) == otrMode) - return; - sessionOtrs.put(account, session, otrMode); - SessionState state = sessionStates.get(account, session); - DataForm dataForm = Feature.createDataForm(DataFormType.form); - Feature.addLoggingField(dataForm, otrMode.getLoggingValues(), - otrMode.getLoggingValues()[0]); - Feature.addDisclosureField(dataForm, DisclosureValue.values(), - otrMode.getDisclosureValue()); - Feature.addSecurityField(dataForm, SecurityValue.values(), - otrMode.getSecurityValue()); - if (state == null || state == SessionState.requesting) { - sessionStates.put(account, session, SessionState.requesting); - Feature.addAcceptField(dataForm, true); - } else { - sessionStates.put(account, session, SessionState.renegotiation); - Feature.addRenegotiateField(dataForm, true); - } - sendFeature(account, user, session, new Feature(dataForm)); - } + /** + * Sets OTR mode for the session and starts negotiation / renegotiation. + * + * @param account + * @param user + * @param session + * @param otrMode + * @throws NetworkException + */ + public void setSessionOtrMode(String account, String user, String session, + OtrMode otrMode) { + if (sessionOtrs.get(account, session) == otrMode) + return; + sessionOtrs.put(account, session, otrMode); + SessionState state = sessionStates.get(account, session); + DataForm dataForm = Feature.createDataForm(DataFormType.form); + Feature.addLoggingField(dataForm, otrMode.getLoggingValues(), + otrMode.getLoggingValues()[0]); + Feature.addDisclosureField(dataForm, DisclosureValue.values(), + otrMode.getDisclosureValue()); + Feature.addSecurityField(dataForm, SecurityValue.values(), + otrMode.getSecurityValue()); + if (state == null || state == SessionState.requesting) { + sessionStates.put(account, session, SessionState.requesting); + Feature.addAcceptField(dataForm, true); + } else { + sessionStates.put(account, session, SessionState.renegotiation); + Feature.addRenegotiateField(dataForm, true); + } + sendFeature(account, user, session, new Feature(dataForm)); + } - private void sendFeature(String account, String user, String session, - Feature feature) { - Message message = new Message(user, Message.Type.normal); - message.setThread(session); - message.addExtension(feature); - try { - ConnectionManager.getInstance().sendPacket(account, message); - } catch (NetworkException e) { - } - } + private void sendFeature(String account, String user, String session, + Feature feature) { + Message message = new Message(user, Message.Type.normal); + message.setThread(session); + message.addExtension(feature); + try { + ConnectionManager.getInstance().sendPacket(account, message); + } catch (NetworkException e) { + } + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/ssn/SessionState.java b/app/src/main/java/com/xabber/android/data/extension/ssn/SessionState.java index d6e500afd7..e3aa7f1743 100644 --- a/app/src/main/java/com/xabber/android/data/extension/ssn/SessionState.java +++ b/app/src/main/java/com/xabber/android/data/extension/ssn/SessionState.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,20 +16,20 @@ public enum SessionState { - /** - * Data form of type "form" with an "accept" field has been sent. - */ - requesting, + /** + * Data form of type "form" with an "accept" field has been sent. + */ + requesting, - /** - * Session has been confirmed (either result or submit has been received). - */ - active, + /** + * Session has been confirmed (either result or submit has been received). + */ + active, - /** - * Data form of type "form" with a "renegotiate" field whose value is "1" - * has been sent. - */ - renegotiation; + /** + * Data form of type "form" with a "renegotiate" field whose value is "1" + * has been sent. + */ + renegotiation } diff --git a/app/src/main/java/com/xabber/android/data/extension/time/OnTimeReceivedListener.java b/app/src/main/java/com/xabber/android/data/extension/time/OnTimeReceivedListener.java index 22890bdeaa..8e6d15a289 100644 --- a/app/src/main/java/com/xabber/android/data/extension/time/OnTimeReceivedListener.java +++ b/app/src/main/java/com/xabber/android/data/extension/time/OnTimeReceivedListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -19,17 +19,16 @@ /** * Listen for time to be synchronized with server the server. - * + * * @author alexander.ivanov - * */ public interface OnTimeReceivedListener extends BaseManagerInterface { - /** - * Server time has been received. - * - * @param connection - */ - void onTimeReceived(ConnectionItem connection); + /** + * Server time has been received. + * + * @param connection + */ + void onTimeReceived(ConnectionItem connection); } diff --git a/app/src/main/java/com/xabber/android/data/extension/time/TimeManager.java b/app/src/main/java/com/xabber/android/data/extension/time/TimeManager.java index 1e92f7c23f..3dabe5619a 100644 --- a/app/src/main/java/com/xabber/android/data/extension/time/TimeManager.java +++ b/app/src/main/java/com/xabber/android/data/extension/time/TimeManager.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -41,155 +41,154 @@ /** * Manage server time and response with local time. - * + * * @author alexander.ivanov - * */ public class TimeManager implements OnServerInfoReceivedListener, - OnPacketListener, OnResponseListener { - - private static final String FEATURE = "urn:xmpp:time"; - - /** - * Offset from server time per account. - */ - private final Map offsets; - - /** - * Time when request has been sent per account. - */ - private final Map sents; - - private final static TimeManager instance; - - static { - instance = new TimeManager(); - Application.getInstance().addManager(instance); - - Connection - .addConnectionCreationListener(new ConnectionCreationListener() { - @Override - public void connectionCreated(final Connection connection) { - ServiceDiscoveryManager.getInstanceFor(connection) - .addFeature(FEATURE); - } - }); - } - - public static TimeManager getInstance() { - return instance; - } - - private TimeManager() { - super(); - offsets = new HashMap(); - sents = new HashMap(); - } - - @Override - public void onServerInfoReceived(ConnectionItem connection) { - if (!(connection instanceof AccountItem)) { - onAvailable(connection); - return; - } - String account = ((AccountItem) connection).getAccount(); - if (ServerInfoManager.getInstance().isProtocolSupported(account, - FEATURE) - && offsets.get(account) == null) { - sents.put(account, new Date()); - Time packet = new Time(); - packet.setTo(Jid.getServer(account)); - packet.setType(Type.GET); - try { - ConnectionManager.getInstance().sendRequest(account, packet, - this); - } catch (NetworkException e) { - } - return; - } - onAvailable(connection); - } - - @Override - public void onPacket(ConnectionItem connection, final String bareAddress, - Packet packet) { - if (!(connection instanceof AccountItem)) - return; - String account = ((AccountItem) connection).getAccount(); - if (!(packet instanceof Time)) - return; - Time time = (Time) packet; - if (time.getType() == Type.GET) { - Time result = new Time(); - result.setType(Type.RESULT); - result.setPacketID(time.getPacketID()); - result.setFrom(time.getTo()); - result.setTo(time.getFrom()); - Calendar calendar = Calendar.getInstance(); - result.setTzo((calendar.get(Calendar.ZONE_OFFSET) + calendar - .get(Calendar.DST_OFFSET)) / 60000); - result.setUtc(calendar.getTime()); - try { - ConnectionManager.getInstance().sendPacket(account, result); - } catch (NetworkException e) { - } - } - } - - private void onAvailable(ConnectionItem connection) { - for (OnTimeReceivedListener listener : Application.getInstance() - .getManagers(OnTimeReceivedListener.class)) - listener.onTimeReceived(connection); - } - - @Override - public void onReceived(String account, String packetId, IQ iq) { - if (!(iq instanceof Time) || !((Time) iq).isValid()) { - onError(account, packetId, iq); - return; - } - Date t1 = sents.remove(account); - Date t2_3 = ((Time) iq).getUtc(); - Date t4 = ((Time) iq).getCreated(); - long offset = ((t2_3.getTime() - t1.getTime()) + (t2_3.getTime() - t4 - .getTime())) / 2; - offsets.put(account, offset); - onAvailable(AccountManager.getInstance().getAccount(account)); - } - - @Override - public void onError(String account, String packetId, IQ iq) { - onDisconnect(account, packetId); - offsets.put(account, 0l); - onAvailable(AccountManager.getInstance().getAccount(account)); - } - - @Override - public void onTimeout(String account, String packetId) { - onError(account, packetId, null); - - } - - @Override - public void onDisconnect(String account, String packetId) { - sents.remove(account); - } - - /** - * Gets difference between local and server time. - * - * @param account - * @return 0 if there is no information about offset. - */ - public long getServerTimeOffset(String account) { - Long value = offsets.get(account); - if (value == null) - return 0l; - return value; - } - - public Date getServerTime(String account) { - return new Date(new Date().getTime() + getServerTimeOffset(account)); - } + OnPacketListener, OnResponseListener { + + private static final String FEATURE = "urn:xmpp:time"; + + /** + * Offset from server time per account. + */ + private final Map offsets; + + /** + * Time when request has been sent per account. + */ + private final Map sents; + + private final static TimeManager instance; + + static { + instance = new TimeManager(); + Application.getInstance().addManager(instance); + + Connection + .addConnectionCreationListener(new ConnectionCreationListener() { + @Override + public void connectionCreated(final Connection connection) { + ServiceDiscoveryManager.getInstanceFor(connection) + .addFeature(FEATURE); + } + }); + } + + public static TimeManager getInstance() { + return instance; + } + + private TimeManager() { + super(); + offsets = new HashMap(); + sents = new HashMap(); + } + + @Override + public void onServerInfoReceived(ConnectionItem connection) { + if (!(connection instanceof AccountItem)) { + onAvailable(connection); + return; + } + String account = ((AccountItem) connection).getAccount(); + if (ServerInfoManager.getInstance().isProtocolSupported(account, + FEATURE) + && offsets.get(account) == null) { + sents.put(account, new Date()); + Time packet = new Time(); + packet.setTo(Jid.getServer(account)); + packet.setType(Type.GET); + try { + ConnectionManager.getInstance().sendRequest(account, packet, + this); + } catch (NetworkException e) { + } + return; + } + onAvailable(connection); + } + + @Override + public void onPacket(ConnectionItem connection, final String bareAddress, + Packet packet) { + if (!(connection instanceof AccountItem)) + return; + String account = ((AccountItem) connection).getAccount(); + if (!(packet instanceof Time)) + return; + Time time = (Time) packet; + if (time.getType() == Type.GET) { + Time result = new Time(); + result.setType(Type.RESULT); + result.setPacketID(time.getPacketID()); + result.setFrom(time.getTo()); + result.setTo(time.getFrom()); + Calendar calendar = Calendar.getInstance(); + result.setTzo((calendar.get(Calendar.ZONE_OFFSET) + calendar + .get(Calendar.DST_OFFSET)) / 60000); + result.setUtc(calendar.getTime()); + try { + ConnectionManager.getInstance().sendPacket(account, result); + } catch (NetworkException e) { + } + } + } + + private void onAvailable(ConnectionItem connection) { + for (OnTimeReceivedListener listener : Application.getInstance() + .getManagers(OnTimeReceivedListener.class)) + listener.onTimeReceived(connection); + } + + @Override + public void onReceived(String account, String packetId, IQ iq) { + if (!(iq instanceof Time) || !((Time) iq).isValid()) { + onError(account, packetId, iq); + return; + } + Date t1 = sents.remove(account); + Date t2_3 = ((Time) iq).getUtc(); + Date t4 = ((Time) iq).getCreated(); + long offset = ((t2_3.getTime() - t1.getTime()) + (t2_3.getTime() - t4 + .getTime())) / 2; + offsets.put(account, offset); + onAvailable(AccountManager.getInstance().getAccount(account)); + } + + @Override + public void onError(String account, String packetId, IQ iq) { + onDisconnect(account, packetId); + offsets.put(account, 0l); + onAvailable(AccountManager.getInstance().getAccount(account)); + } + + @Override + public void onTimeout(String account, String packetId) { + onError(account, packetId, null); + + } + + @Override + public void onDisconnect(String account, String packetId) { + sents.remove(account); + } + + /** + * Gets difference between local and server time. + * + * @param account + * @return 0 if there is no information about offset. + */ + public long getServerTimeOffset(String account) { + Long value = offsets.get(account); + if (value == null) + return 0l; + return value; + } + + public Date getServerTime(String account) { + return new Date(new Date().getTime() + getServerTimeOffset(account)); + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/vcard/OnVCardListener.java b/app/src/main/java/com/xabber/android/data/extension/vcard/OnVCardListener.java index 423898d271..0ef381143c 100644 --- a/app/src/main/java/com/xabber/android/data/extension/vcard/OnVCardListener.java +++ b/app/src/main/java/com/xabber/android/data/extension/vcard/OnVCardListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -19,27 +19,26 @@ /** * Listener for vCard to be received. - * + * * @author alexander.ivanov - * */ public interface OnVCardListener extends BaseUIListener { - /** - * Requested vCard has been received. - * - * @param account - * @param bareAddress - * @param vCard - */ - public void onVCardReceived(String account, String bareAddress, VCard vCard); + /** + * Requested vCard has been received. + * + * @param account + * @param bareAddress + * @param vCard + */ + void onVCardReceived(String account, String bareAddress, VCard vCard); - /** - * Fail occurred on vCard response. - * - * @param account - * @param bareAddress - */ - public void onVCardFailed(String account, String bareAddress); + /** + * Fail occurred on vCard response. + * + * @param account + * @param bareAddress + */ + void onVCardFailed(String account, String bareAddress); } diff --git a/app/src/main/java/com/xabber/android/data/extension/vcard/VCardManager.java b/app/src/main/java/com/xabber/android/data/extension/vcard/VCardManager.java index 13429737d3..78174d45a6 100644 --- a/app/src/main/java/com/xabber/android/data/extension/vcard/VCardManager.java +++ b/app/src/main/java/com/xabber/android/data/extension/vcard/VCardManager.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -51,273 +51,271 @@ /** * Manage vCards and there requests. - * + * * @author alexander.ivanov - * */ public class VCardManager implements OnLoadListener, OnPacketListener, - OnDisconnectListener, OnRosterReceivedListener, - OnAccountRemovedListener { + OnDisconnectListener, OnRosterReceivedListener, + OnAccountRemovedListener { - private static final StructuredName EMPTY_STRUCTURED_NAME = new StructuredName( - null, null, null, null, null); + private static final StructuredName EMPTY_STRUCTURED_NAME = new StructuredName( + null, null, null, null, null); - /** - * Sent requests. - */ - private final Collection requests; + /** + * Sent requests. + */ + private final Collection requests; - /** - * List of invalid avatar's hashes (hashes without actual avatars). - */ - private final Set invalidHashes; + /** + * List of invalid avatar's hashes (hashes without actual avatars). + */ + private final Set invalidHashes; - /** - * Nick and formatted names for the users. - */ - private final Map names; + /** + * Nick and formatted names for the users. + */ + private final Map names; - /** - * List of accounts which requests its avatar in order to avoid subsequence - * requests. - */ - private final ArrayList accountRequested; + /** + * List of accounts which requests its avatar in order to avoid subsequence + * requests. + */ + private final ArrayList accountRequested; - private final static VCardManager instance; + private final static VCardManager instance; - static { - instance = new VCardManager(); - Application.getInstance().addManager(instance); - } + static { + instance = new VCardManager(); + Application.getInstance().addManager(instance); + } - public static VCardManager getInstance() { - return instance; - } + public static VCardManager getInstance() { + return instance; + } - private VCardManager() { - requests = new ArrayList(); - invalidHashes = new HashSet(); - names = new HashMap(); - accountRequested = new ArrayList(); - } + private VCardManager() { + requests = new ArrayList(); + invalidHashes = new HashSet(); + names = new HashMap(); + accountRequested = new ArrayList(); + } - @Override - public void onLoad() { - final Map names = new HashMap(); - Cursor cursor = VCardTable.getInstance().list(); - try { - if (cursor.moveToFirst()) { - do { - names.put( - VCardTable.getUser(cursor), - new StructuredName(VCardTable.getNickName(cursor), - VCardTable.getFormattedName(cursor), - VCardTable.getFirstName(cursor), VCardTable - .getMiddleName(cursor), VCardTable - .getLastName(cursor))); - } while (cursor.moveToNext()); - } - } finally { - cursor.close(); - } - Application.getInstance().runOnUiThread(new Runnable() { - @Override - public void run() { - onLoaded(names); - } - }); - } + @Override + public void onLoad() { + final Map names = new HashMap(); + Cursor cursor = VCardTable.getInstance().list(); + try { + if (cursor.moveToFirst()) { + do { + names.put( + VCardTable.getUser(cursor), + new StructuredName(VCardTable.getNickName(cursor), + VCardTable.getFormattedName(cursor), + VCardTable.getFirstName(cursor), VCardTable + .getMiddleName(cursor), VCardTable + .getLastName(cursor))); + } while (cursor.moveToNext()); + } + } finally { + cursor.close(); + } + Application.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + onLoaded(names); + } + }); + } - private void onLoaded(Map names) { - this.names.putAll(names); - } + private void onLoaded(Map names) { + this.names.putAll(names); + } - @Override - public void onRosterReceived(AccountItem accountItem) { - String account = accountItem.getAccount(); - if (!accountRequested.contains(account) - && SettingsManager.connectionLoadVCard()) { - String bareAddress = Jid.getBareAddress(accountItem.getRealJid()); - if (bareAddress != null) { - request(account, bareAddress, null); - accountRequested.add(account); - } - } + @Override + public void onRosterReceived(AccountItem accountItem) { + String account = accountItem.getAccount(); + if (!accountRequested.contains(account) + && SettingsManager.connectionLoadVCard()) { + String bareAddress = Jid.getBareAddress(accountItem.getRealJid()); + if (bareAddress != null) { + request(account, bareAddress, null); + accountRequested.add(account); + } + } - // Request vCards for new contacts. - for (RosterContact contact : RosterManager.getInstance().getContacts()) - if (account.equals(contact.getUser()) - && !names.containsKey(contact.getUser())) - request(account, contact.getUser(), null); - } + // Request vCards for new contacts. + for (RosterContact contact : RosterManager.getInstance().getContacts()) + if (account.equals(contact.getUser()) + && !names.containsKey(contact.getUser())) + request(account, contact.getUser(), null); + } - @Override - public void onDisconnect(ConnectionItem connection) { - if (!(connection instanceof AccountItem)) - return; - String account = ((AccountItem) connection).getAccount(); - Iterator iterator = requests.iterator(); - while (iterator.hasNext()) { - if (iterator.next().getAccount().equals(account)) - iterator.remove(); - } - } + @Override + public void onDisconnect(ConnectionItem connection) { + if (!(connection instanceof AccountItem)) + return; + String account = ((AccountItem) connection).getAccount(); + Iterator iterator = requests.iterator(); + while (iterator.hasNext()) { + if (iterator.next().getAccount().equals(account)) + iterator.remove(); + } + } - @Override - public void onAccountRemoved(AccountItem accountItem) { - accountRequested.remove(accountItem.getAccount()); - } + @Override + public void onAccountRemoved(AccountItem accountItem) { + accountRequested.remove(accountItem.getAccount()); + } - /** - * Requests vCard. - * - * @param account - * @param bareAddress - * @param hash - * avatar's hash that was intent to request vCard. Can be - * null. - */ - public void request(String account, String bareAddress, String hash) { - if (hash != null && invalidHashes.contains(hash)) - return; - // User can change avatar before first request will be completed. - for (VCardRequest check : requests) - if (check.getUser().equals(bareAddress)) { - if (hash != null) - check.addHash(hash); - return; - } - VCard packet = new VCard(); - packet.setTo(bareAddress); - packet.setType(Type.GET); - VCardRequest request = new VCardRequest(account, bareAddress, - packet.getPacketID()); - requests.add(request); - if (hash != null) - request.addHash(hash); - try { - ConnectionManager.getInstance().sendPacket(account, packet); - } catch (NetworkException e) { - requests.remove(request); - onVCardFailed(account, bareAddress); - } - } + /** + * Requests vCard. + * + * @param account + * @param bareAddress + * @param hash avatar's hash that was intent to request vCard. Can be + * null. + */ + public void request(String account, String bareAddress, String hash) { + if (hash != null && invalidHashes.contains(hash)) + return; + // User can change avatar before first request will be completed. + for (VCardRequest check : requests) + if (check.getUser().equals(bareAddress)) { + if (hash != null) + check.addHash(hash); + return; + } + VCard packet = new VCard(); + packet.setTo(bareAddress); + packet.setType(Type.GET); + VCardRequest request = new VCardRequest(account, bareAddress, + packet.getPacketID()); + requests.add(request); + if (hash != null) + request.addHash(hash); + try { + ConnectionManager.getInstance().sendPacket(account, packet); + } catch (NetworkException e) { + requests.remove(request); + onVCardFailed(account, bareAddress); + } + } - /** - * Get uses's nick name. - * - * @param bareAddress - * @return first specified value: - *

    - *
  • nick name
  • - *
  • formatted name
  • - *
  • empty string
  • - *
- */ - public String getName(String bareAddress) { - StructuredName name = names.get(bareAddress); - if (name == null) - return ""; - return name.getBestName(); - } + /** + * Get uses's nick name. + * + * @param bareAddress + * @return first specified value: + *
    + *
  • nick name
  • + *
  • formatted name
  • + *
  • empty string
  • + *
+ */ + public String getName(String bareAddress) { + StructuredName name = names.get(bareAddress); + if (name == null) + return ""; + return name.getBestName(); + } - /** - * Get uses's name information. - * - * @param bareAddress - * @return null if there is no info. - */ - public StructuredName getStructucedName(String bareAddress) { - return names.get(bareAddress); - } + /** + * Get uses's name information. + * + * @param bareAddress + * @return null if there is no info. + */ + public StructuredName getStructucedName(String bareAddress) { + return names.get(bareAddress); + } - private void onVCardReceived(final String account, - final String bareAddress, final VCard vCard) { - for (OnVCardListener listener : Application.getInstance() - .getUIListeners(OnVCardListener.class)) - listener.onVCardReceived(account, bareAddress, vCard); - } + private void onVCardReceived(final String account, + final String bareAddress, final VCard vCard) { + for (OnVCardListener listener : Application.getInstance() + .getUIListeners(OnVCardListener.class)) + listener.onVCardReceived(account, bareAddress, vCard); + } - private void onVCardFailed(final String account, final String bareAddress) { - for (OnVCardListener listener : Application.getInstance() - .getUIListeners(OnVCardListener.class)) - listener.onVCardFailed(account, bareAddress); - } + private void onVCardFailed(final String account, final String bareAddress) { + for (OnVCardListener listener : Application.getInstance() + .getUIListeners(OnVCardListener.class)) + listener.onVCardFailed(account, bareAddress); + } - @Override - public void onPacket(ConnectionItem connection, final String bareAddress, - Packet packet) { - if (!(connection instanceof AccountItem)) - return; - String account = ((AccountItem) connection).getAccount(); - if (packet instanceof Presence - && ((Presence) packet).getType() != Presence.Type.error) { - if (bareAddress == null) - return; - // Request vCard for new users - if (!names.containsKey(bareAddress)) - if (SettingsManager.connectionLoadVCard()) - request(account, bareAddress, null); - } else if (packet instanceof IQ) { - IQ iq = (IQ) packet; - if (iq.getType() != Type.ERROR && !(packet instanceof VCard)) - return; - String packetId = iq.getPacketID(); - VCardRequest request = null; - Iterator iterator = requests.iterator(); - while (iterator.hasNext()) { - VCardRequest check = iterator.next(); - if (check.getPacketId().equals(packetId)) { - request = check; - iterator.remove(); - break; - } - } - if (request == null || !request.getUser().equals(bareAddress)) - return; - final StructuredName name; - if (iq.getType() == Type.ERROR) { - onVCardFailed(account, bareAddress); - invalidHashes.addAll(request.getHashes()); - if (names.containsKey(bareAddress)) - return; - name = EMPTY_STRUCTURED_NAME; - } else if (packet instanceof VCard) { - VCard vCard = (VCard) packet; - onVCardReceived(account, bareAddress, vCard); - String hash = vCard.getAvatarHash(); - for (String check : request.getHashes()) - if (!check.equals(hash)) - invalidHashes.add(check); - AvatarManager.getInstance().onAvatarReceived(bareAddress, hash, - vCard.getAvatar()); - name = new StructuredName(vCard.getNickName(), - vCard.getFormattedName(), vCard.getFirstName(), - vCard.getMiddleName(), vCard.getLastName()); - } else - throw new IllegalStateException(); - names.put(bareAddress, name); - for (RosterContact rosterContact : RosterManager.getInstance() - .getContacts()) - if (rosterContact.getUser().equals(bareAddress)) - for (OnRosterChangedListener listener : Application - .getInstance().getManagers( - OnRosterChangedListener.class)) - listener.onContactStructuredInfoChanged(rosterContact, - name); - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - VCardTable.getInstance().write(bareAddress, name); - } - }); - if (iq.getFrom() == null) { // account it self - AccountManager.getInstance().onAccountChanged(account); - } else { - RosterManager.getInstance().onContactChanged(account, - bareAddress); - } - } - } + @Override + public void onPacket(ConnectionItem connection, final String bareAddress, + Packet packet) { + if (!(connection instanceof AccountItem)) + return; + String account = ((AccountItem) connection).getAccount(); + if (packet instanceof Presence + && ((Presence) packet).getType() != Presence.Type.error) { + if (bareAddress == null) + return; + // Request vCard for new users + if (!names.containsKey(bareAddress)) + if (SettingsManager.connectionLoadVCard()) + request(account, bareAddress, null); + } else if (packet instanceof IQ) { + IQ iq = (IQ) packet; + if (iq.getType() != Type.ERROR && !(packet instanceof VCard)) + return; + String packetId = iq.getPacketID(); + VCardRequest request = null; + Iterator iterator = requests.iterator(); + while (iterator.hasNext()) { + VCardRequest check = iterator.next(); + if (check.getPacketId().equals(packetId)) { + request = check; + iterator.remove(); + break; + } + } + if (request == null || !request.getUser().equals(bareAddress)) + return; + final StructuredName name; + if (iq.getType() == Type.ERROR) { + onVCardFailed(account, bareAddress); + invalidHashes.addAll(request.getHashes()); + if (names.containsKey(bareAddress)) + return; + name = EMPTY_STRUCTURED_NAME; + } else if (packet instanceof VCard) { + VCard vCard = (VCard) packet; + onVCardReceived(account, bareAddress, vCard); + String hash = vCard.getAvatarHash(); + for (String check : request.getHashes()) + if (!check.equals(hash)) + invalidHashes.add(check); + AvatarManager.getInstance().onAvatarReceived(bareAddress, hash, + vCard.getAvatar()); + name = new StructuredName(vCard.getNickName(), + vCard.getFormattedName(), vCard.getFirstName(), + vCard.getMiddleName(), vCard.getLastName()); + } else + throw new IllegalStateException(); + names.put(bareAddress, name); + for (RosterContact rosterContact : RosterManager.getInstance() + .getContacts()) + if (rosterContact.getUser().equals(bareAddress)) + for (OnRosterChangedListener listener : Application + .getInstance().getManagers( + OnRosterChangedListener.class)) + listener.onContactStructuredInfoChanged(rosterContact, + name); + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + VCardTable.getInstance().write(bareAddress, name); + } + }); + if (iq.getFrom() == null) { // account it self + AccountManager.getInstance().onAccountChanged(account); + } else { + RosterManager.getInstance().onContactChanged(account, + bareAddress); + } + } + } } diff --git a/app/src/main/java/com/xabber/android/data/extension/vcard/VCardRequest.java b/app/src/main/java/com/xabber/android/data/extension/vcard/VCardRequest.java index 03cf265cd5..aa29ff74ac 100644 --- a/app/src/main/java/com/xabber/android/data/extension/vcard/VCardRequest.java +++ b/app/src/main/java/com/xabber/android/data/extension/vcard/VCardRequest.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -22,35 +22,34 @@ /** * Store information about vCard request for specified user. - * + * * @author alexander.ivanov - * */ class VCardRequest extends BaseEntity { - private final String packetId; + private final String packetId; - /** - * List of intent avatar's hashes. - */ - private final HashSet hashes; + /** + * List of intent avatar's hashes. + */ + private final HashSet hashes; - public VCardRequest(String account, String bareAddress, String packetId) { - super(account, bareAddress); - this.packetId = packetId; - this.hashes = new HashSet(); - } + public VCardRequest(String account, String bareAddress, String packetId) { + super(account, bareAddress); + this.packetId = packetId; + this.hashes = new HashSet(); + } - public String getPacketId() { - return packetId; - } + public String getPacketId() { + return packetId; + } - public Collection getHashes() { - return Collections.unmodifiableCollection(hashes); - } + public Collection getHashes() { + return Collections.unmodifiableCollection(hashes); + } - public void addHash(String hash) { - hashes.add(hash); - } + public void addHash(String hash) { + hashes.add(hash); + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/data/extension/vcard/VCardTable.java b/app/src/main/java/com/xabber/android/data/extension/vcard/VCardTable.java index bd26ed1d7b..adab176ea3 100644 --- a/app/src/main/java/com/xabber/android/data/extension/vcard/VCardTable.java +++ b/app/src/main/java/com/xabber/android/data/extension/vcard/VCardTable.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -25,142 +25,142 @@ /** * Storage with useful vcard fields. - * + * * @author alexander.ivanov */ class VCardTable extends AbstractTable { - private static final class Fields implements BaseColumns { - - private Fields() { - } - - public static final String USER = "user"; - public static final String NICK_NAME = "nick_name"; - public static final String FORMATTED_NAME = "formatted_name"; - public static final String FIRST_NAME = "first_name"; - public static final String MIDDLE_NAME = "middle_name"; - public static final String LAST_NAME = "last_name"; - - } - - private static final String NAME = "vcards"; - private static final String[] PROJECTION = new String[] { Fields.USER, - Fields.NICK_NAME, Fields.FORMATTED_NAME, Fields.FIRST_NAME, - Fields.MIDDLE_NAME, Fields.LAST_NAME }; - - private final DatabaseManager databaseManager; - private SQLiteStatement writeStatement; - private final Object writeLock; - - private final static VCardTable instance; - - static { - instance = new VCardTable(DatabaseManager.getInstance()); - DatabaseManager.getInstance().addTable(instance); - } - - public static VCardTable getInstance() { - return instance; - } - - private VCardTable(DatabaseManager databaseManager) { - this.databaseManager = databaseManager; - writeStatement = null; - writeLock = new Object(); - } - - @Override - public void create(SQLiteDatabase db) { - String sql = "CREATE TABLE " + NAME + " (" + Fields.USER - + " TEXT PRIMARY KEY," + Fields.NICK_NAME + " TEXT," - + Fields.FORMATTED_NAME + " TEXT," + Fields.FIRST_NAME - + " TEXT," + Fields.MIDDLE_NAME + " TEXT," + Fields.LAST_NAME - + " TEXT);"; - DatabaseManager.execSQL(db, sql); - } - - @Override - public void migrate(SQLiteDatabase db, int toVersion) { - super.migrate(db, toVersion); - String sql; - switch (toVersion) { - case 11: - sql = "CREATE TABLE vcards (" + "user TEXT PRIMARY KEY," - + "nick_name TEXT," + "formatted_name TEXT);"; - DatabaseManager.execSQL(db, sql); - break; - case 44: - sql = "UPDATE vcards SET nick_name = \"\" WHERE nick_name IS NULL;"; - DatabaseManager.execSQL(db, sql); - sql = "UPDATE vcards SET formatted_name = \"\" WHERE formatted_name IS NULL;"; - DatabaseManager.execSQL(db, sql); - break; - case 49: - DatabaseManager.dropTable(db, "vcards"); - sql = "CREATE TABLE vcards (" + "user TEXT PRIMARY KEY," - + "nick_name TEXT," + "formatted_name TEXT," - + "first_name TEXT," + "middle_name TEXT," - + "last_name TEXT);"; - DatabaseManager.execSQL(db, sql); - break; - default: - break; - } - } - - void write(String bareAddress, StructuredName name) { - synchronized (writeLock) { - if (writeStatement == null) { - SQLiteDatabase db = databaseManager.getWritableDatabase(); - writeStatement = db.compileStatement("INSERT OR REPLACE INTO " - + NAME + " (" + Fields.USER + ", " + Fields.NICK_NAME - + ", " + Fields.FORMATTED_NAME + ", " - + Fields.FIRST_NAME + ", " + Fields.MIDDLE_NAME + ", " - + Fields.LAST_NAME + ") VALUES (?, ?, ?, ?, ?, ?);"); - } - writeStatement.bindString(1, bareAddress); - writeStatement.bindString(2, name.getNickName()); - writeStatement.bindString(3, name.getFormattedName()); - writeStatement.bindString(4, name.getFirstName()); - writeStatement.bindString(5, name.getMiddleName()); - writeStatement.bindString(6, name.getLastName()); - writeStatement.execute(); - } - } - - @Override - protected String getTableName() { - return NAME; - } - - @Override - protected String[] getProjection() { - return PROJECTION; - } - - static String getUser(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.USER)); - } - - static String getNickName(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.NICK_NAME)); - } - - static String getFormattedName(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.FORMATTED_NAME)); - } - - static String getFirstName(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.FIRST_NAME)); - } - - static String getMiddleName(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.MIDDLE_NAME)); - } - - static String getLastName(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.LAST_NAME)); - } + private static final class Fields implements BaseColumns { + + private Fields() { + } + + public static final String USER = "user"; + public static final String NICK_NAME = "nick_name"; + public static final String FORMATTED_NAME = "formatted_name"; + public static final String FIRST_NAME = "first_name"; + public static final String MIDDLE_NAME = "middle_name"; + public static final String LAST_NAME = "last_name"; + + } + + private static final String NAME = "vcards"; + private static final String[] PROJECTION = new String[]{Fields.USER, + Fields.NICK_NAME, Fields.FORMATTED_NAME, Fields.FIRST_NAME, + Fields.MIDDLE_NAME, Fields.LAST_NAME}; + + private final DatabaseManager databaseManager; + private SQLiteStatement writeStatement; + private final Object writeLock; + + private final static VCardTable instance; + + static { + instance = new VCardTable(DatabaseManager.getInstance()); + DatabaseManager.getInstance().addTable(instance); + } + + public static VCardTable getInstance() { + return instance; + } + + private VCardTable(DatabaseManager databaseManager) { + this.databaseManager = databaseManager; + writeStatement = null; + writeLock = new Object(); + } + + @Override + public void create(SQLiteDatabase db) { + String sql = "CREATE TABLE " + NAME + " (" + Fields.USER + + " TEXT PRIMARY KEY," + Fields.NICK_NAME + " TEXT," + + Fields.FORMATTED_NAME + " TEXT," + Fields.FIRST_NAME + + " TEXT," + Fields.MIDDLE_NAME + " TEXT," + Fields.LAST_NAME + + " TEXT);"; + DatabaseManager.execSQL(db, sql); + } + + @Override + public void migrate(SQLiteDatabase db, int toVersion) { + super.migrate(db, toVersion); + String sql; + switch (toVersion) { + case 11: + sql = "CREATE TABLE vcards (" + "user TEXT PRIMARY KEY," + + "nick_name TEXT," + "formatted_name TEXT);"; + DatabaseManager.execSQL(db, sql); + break; + case 44: + sql = "UPDATE vcards SET nick_name = \"\" WHERE nick_name IS NULL;"; + DatabaseManager.execSQL(db, sql); + sql = "UPDATE vcards SET formatted_name = \"\" WHERE formatted_name IS NULL;"; + DatabaseManager.execSQL(db, sql); + break; + case 49: + DatabaseManager.dropTable(db, "vcards"); + sql = "CREATE TABLE vcards (" + "user TEXT PRIMARY KEY," + + "nick_name TEXT," + "formatted_name TEXT," + + "first_name TEXT," + "middle_name TEXT," + + "last_name TEXT);"; + DatabaseManager.execSQL(db, sql); + break; + default: + break; + } + } + + void write(String bareAddress, StructuredName name) { + synchronized (writeLock) { + if (writeStatement == null) { + SQLiteDatabase db = databaseManager.getWritableDatabase(); + writeStatement = db.compileStatement("INSERT OR REPLACE INTO " + + NAME + " (" + Fields.USER + ", " + Fields.NICK_NAME + + ", " + Fields.FORMATTED_NAME + ", " + + Fields.FIRST_NAME + ", " + Fields.MIDDLE_NAME + ", " + + Fields.LAST_NAME + ") VALUES (?, ?, ?, ?, ?, ?);"); + } + writeStatement.bindString(1, bareAddress); + writeStatement.bindString(2, name.getNickName()); + writeStatement.bindString(3, name.getFormattedName()); + writeStatement.bindString(4, name.getFirstName()); + writeStatement.bindString(5, name.getMiddleName()); + writeStatement.bindString(6, name.getLastName()); + writeStatement.execute(); + } + } + + @Override + protected String getTableName() { + return NAME; + } + + @Override + protected String[] getProjection() { + return PROJECTION; + } + + static String getUser(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.USER)); + } + + static String getNickName(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.NICK_NAME)); + } + + static String getFormattedName(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.FORMATTED_NAME)); + } + + static String getFirstName(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.FIRST_NAME)); + } + + static String getMiddleName(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.MIDDLE_NAME)); + } + + static String getLastName(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.LAST_NAME)); + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/data/intent/AccountIntentBuilder.java b/app/src/main/java/com/xabber/android/data/intent/AccountIntentBuilder.java index ad2a488ab7..12365bf6b2 100644 --- a/app/src/main/java/com/xabber/android/data/intent/AccountIntentBuilder.java +++ b/app/src/main/java/com/xabber/android/data/intent/AccountIntentBuilder.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,15 +18,14 @@ /** * Intent builder with account field. - * + * * @author alexander.ivanov - * */ public class AccountIntentBuilder extends - BaseAccountIntentBuilder { + BaseAccountIntentBuilder { - public AccountIntentBuilder(Context context, Class cls) { - super(context, cls); - } + public AccountIntentBuilder(Context context, Class cls) { + super(context, cls); + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/data/intent/BaseAccountIntentBuilder.java b/app/src/main/java/com/xabber/android/data/intent/BaseAccountIntentBuilder.java index 21ce4b3f03..735febe592 100644 --- a/app/src/main/java/com/xabber/android/data/intent/BaseAccountIntentBuilder.java +++ b/app/src/main/java/com/xabber/android/data/intent/BaseAccountIntentBuilder.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,32 +18,32 @@ import android.content.Intent; class BaseAccountIntentBuilder> extends - SegmentIntentBuilder { - - private String account; - - public BaseAccountIntentBuilder(Context context, Class cls) { - super(context, cls); - } - - @SuppressWarnings("unchecked") - public T setAccount(String account) { - this.account = account; - return (T) this; - } - - @Override - void preBuild() { - super.preBuild(); - if (account == null) - return; - if (getSegmentCount() != 0) - throw new IllegalStateException(); - addSegment(account); - } - - public static String getAccount(Intent intent) { - return getSegment(intent, 0); - } + SegmentIntentBuilder { + + private String account; + + public BaseAccountIntentBuilder(Context context, Class cls) { + super(context, cls); + } + + @SuppressWarnings("unchecked") + public T setAccount(String account) { + this.account = account; + return (T) this; + } + + @Override + void preBuild() { + super.preBuild(); + if (account == null) + return; + if (getSegmentCount() != 0) + throw new IllegalStateException(); + addSegment(account); + } + + public static String getAccount(Intent intent) { + return getSegment(intent, 0); + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/data/intent/BaseIntentBuilder.java b/app/src/main/java/com/xabber/android/data/intent/BaseIntentBuilder.java index f66d201544..38fecb6eea 100644 --- a/app/src/main/java/com/xabber/android/data/intent/BaseIntentBuilder.java +++ b/app/src/main/java/com/xabber/android/data/intent/BaseIntentBuilder.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -19,20 +19,20 @@ public class BaseIntentBuilder> { - private final Context context; - private final Class cls; + private final Context context; + private final Class cls; - public BaseIntentBuilder(Context context, Class cls) { - super(); - this.context = context; - this.cls = cls; - } + public BaseIntentBuilder(Context context, Class cls) { + super(); + this.context = context; + this.cls = cls; + } - public Intent build() { - if (context != null || cls != null) - return new Intent(context, cls); - else - return new Intent(); - } + public Intent build() { + if (context != null || cls != null) + return new Intent(context, cls); + else + return new Intent(); + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/data/intent/EntityIntentBuilder.java b/app/src/main/java/com/xabber/android/data/intent/EntityIntentBuilder.java index d128a549f2..a09902aa9a 100644 --- a/app/src/main/java/com/xabber/android/data/intent/EntityIntentBuilder.java +++ b/app/src/main/java/com/xabber/android/data/intent/EntityIntentBuilder.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -19,36 +19,35 @@ /** * Intent builder with account and user fields. - * + * * @author alexander.ivanov - * */ public class EntityIntentBuilder extends - BaseAccountIntentBuilder { - - public EntityIntentBuilder(Context context, Class cls) { - super(context, cls); - } - - private String user; - - public EntityIntentBuilder setUser(String user) { - this.user = user; - return this; - } - - @Override - void preBuild() { - super.preBuild(); - if (user == null) - return; - if (getSegmentCount() == 0) - throw new IllegalStateException(); - addSegment(user); - } - - public static String getUser(Intent intent) { - return getSegment(intent, 1); - } + BaseAccountIntentBuilder { + + public EntityIntentBuilder(Context context, Class cls) { + super(context, cls); + } + + private String user; + + public EntityIntentBuilder setUser(String user) { + this.user = user; + return this; + } + + @Override + void preBuild() { + super.preBuild(); + if (user == null) + return; + if (getSegmentCount() == 0) + throw new IllegalStateException(); + addSegment(user); + } + + public static String getUser(Intent intent) { + return getSegment(intent, 1); + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/data/intent/SegmentIntentBuilder.java b/app/src/main/java/com/xabber/android/data/intent/SegmentIntentBuilder.java index 3cf01e4d02..a07729e20e 100644 --- a/app/src/main/java/com/xabber/android/data/intent/SegmentIntentBuilder.java +++ b/app/src/main/java/com/xabber/android/data/intent/SegmentIntentBuilder.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -23,69 +23,69 @@ import android.net.Uri; public class SegmentIntentBuilder> extends - BaseIntentBuilder { + BaseIntentBuilder { - private final List segments; + private final List segments; - public SegmentIntentBuilder(Context context, Class cls) { - super(context, cls); - segments = new ArrayList(); - } + public SegmentIntentBuilder(Context context, Class cls) { + super(context, cls); + segments = new ArrayList(); + } - protected int getSegmentCount() { - return segments.size(); - } + protected int getSegmentCount() { + return segments.size(); + } - @SuppressWarnings("unchecked") - public T addSegment(String segment) { - segments.add(segment); - return (T) this; - } + @SuppressWarnings("unchecked") + public T addSegment(String segment) { + segments.add(segment); + return (T) this; + } - void preBuild() { - } + void preBuild() { + } - @Override - public Intent build() { - preBuild(); - Intent intent = super.build(); - Uri.Builder builder = new Uri.Builder(); - for (String segment : segments) - builder.appendPath(segment); - Uri uri = builder.build(); - uri = Uri.parse(uri.toString()); // Workaround for android 1.5 - intent.setData(uri); - return intent; - } + @Override + public Intent build() { + preBuild(); + Intent intent = super.build(); + Uri.Builder builder = new Uri.Builder(); + for (String segment : segments) + builder.appendPath(segment); + Uri uri = builder.build(); + uri = Uri.parse(uri.toString()); // Workaround for android 1.5 + intent.setData(uri); + return intent; + } - /** - * Parse segments from the intent. - * - * @param intent - * @return - */ - static List getSegments(Intent intent) { - Uri uri = intent.getData(); - if (uri == null) { - List emptyList = Collections.emptyList(); - return emptyList; - } - return uri.getPathSegments(); - } + /** + * Parse segments from the intent. + * + * @param intent + * @return + */ + static List getSegments(Intent intent) { + Uri uri = intent.getData(); + if (uri == null) { + List emptyList = Collections.emptyList(); + return emptyList; + } + return uri.getPathSegments(); + } - /** - * @param intent - * @param index - * @return Segment from the intent data uri or null. - */ - public static String getSegment(Intent intent, int index) { - Uri uri = intent.getData(); - if (uri == null) - return null; - List list = uri.getPathSegments(); - if (list.size() <= index) - return null; - return list.get(index); - } + /** + * @param intent + * @param index + * @return Segment from the intent data uri or null. + */ + public static String getSegment(Intent intent, int index) { + Uri uri = intent.getData(); + if (uri == null) + return null; + List list = uri.getPathSegments(); + if (list.size() <= index) + return null; + return list.get(index); + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/data/message/AbstractChat.java b/app/src/main/java/com/xabber/android/data/message/AbstractChat.java index b4ebd8db02..21096862b6 100644 --- a/app/src/main/java/com/xabber/android/data/message/AbstractChat.java +++ b/app/src/main/java/com/xabber/android/data/message/AbstractChat.java @@ -1,33 +1,19 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data.message; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; - -import org.jivesoftware.smack.packet.Message; -import org.jivesoftware.smack.packet.Message.Type; -import org.jivesoftware.smack.packet.Packet; -import org.jivesoftware.smack.util.StringUtils; -import org.jivesoftware.smackx.packet.DelayInformation; - import android.database.Cursor; import com.xabber.android.data.Application; @@ -44,649 +30,660 @@ import com.xabber.android.data.message.chat.ChatManager; import com.xabber.android.data.notification.NotificationManager; import com.xabber.xmpp.archive.SaveMode; +import com.xabber.xmpp.carbon.CarbonManager; + +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.packet.Message.Type; +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smack.util.StringUtils; +import org.jivesoftware.smackx.packet.DelayInformation; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; /** * Chat instance. - * + * * @author alexander.ivanov - * */ public abstract class AbstractChat extends BaseEntity { - /** - * Message tag used when server side record is disable. - */ - private static final String NO_RECORD_TAG = "com.xabber.android.data.message.NO_RECORD_TAG"; - - /** - * Number of messages from history to be shown for context purpose. - */ - private static final int PRELOADED_MESSAGES = 3; - - /** - * Current thread id. - */ - private String threadId; - - /** - * Whether chat is open and should be displayed as active chat. - */ - protected boolean active; - - /** - * Whether changes in status should be record. - */ - protected boolean trackStatus; - - /** - * Whether user never received notifications from this chat. - */ - protected boolean firstNotification; - - /** - * Last incoming message's text. - */ - protected String lastText; - - /** - * Last message's time. - */ - protected Date lastTime; - - /** - * Ids of messages not loaded in to the memory. - * - * MUST BE ACCESSED FROM BACKGROUND THREAD ONLY. - */ - protected final Collection historyIds; - - /** - * Sorted list of messages in this chat. - */ - protected final List messages; - - /** - * List of messages to be sent. - */ - protected final Collection sendQuery; - - protected AbstractChat(final String account, final String user) { - super(account, user); - threadId = StringUtils.randomString(12); - active = false; - trackStatus = false; - firstNotification = true; - lastText = ""; - lastTime = null; - historyIds = new ArrayList(); - messages = new ArrayList(); - sendQuery = new ArrayList(); - - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - loadMessages(); - } - }); - } - - /** - * Load recent messages from local history. - * - * CALL THIS METHOD FROM BACKGROUND THREAD ONLY. - */ - private void loadMessages() { - final ArrayList messageItems = new ArrayList(); - LinkedList linkedList = new LinkedList(); - boolean started = false; - Cursor cursor = MessageTable.getInstance().list(account, user); - try { - if (cursor.moveToFirst()) { - do { - MessageItem messageItem = createMessageItem(cursor); - if (started) { - messageItems.add(messageItem); - continue; - } - linkedList.addLast(messageItem); - if (messageItem.isRead() && messageItem.isSent()) { - if (linkedList.size() <= PRELOADED_MESSAGES) - continue; - messageItem = linkedList.removeFirst(); - historyIds.add(messageItem.getId()); - continue; - } - started = true; - messageItems.addAll(linkedList); - } while (cursor.moveToNext()); - } - } finally { - cursor.close(); - } - if (!started) - messageItems.addAll(linkedList); - Application.getInstance().runOnUiThread(new Runnable() { - @Override - public void run() { - for (MessageItem messageItem : messageItems) - updateSendQuery(messageItem); - addMessages(messageItems); - } - }); - } - - /** - * Update existing message list with loaded. - * - * @param messageItems - */ - private void addMessages(Collection messageItems) { - messages.addAll(messageItems); - sort(); - MessageManager.getInstance().onChatChanged(account, user, false); - } - - /** - * @param cursor - * @return New message item. - */ - private MessageItem createMessageItem(Cursor cursor) { - MessageItem messageItem = new MessageItem(this, - MessageTable.getTag(cursor), MessageTable.getResource(cursor), - MessageTable.getText(cursor), MessageTable.getAction(cursor), - MessageTable.getTimeStamp(cursor), - MessageTable.getDelayTimeStamp(cursor), - MessageTable.isIncoming(cursor), MessageTable.isRead(cursor), - MessageTable.isSent(cursor), MessageTable.hasError(cursor), - true, false, false); - messageItem.setId(MessageTable.getId(cursor)); - return messageItem; - } - - /** - * Load all message from local history. - * - * CALL THIS METHOD FROM BACKGROUND THREAD ONLY. - */ - private void loadHistory() { - if (historyIds.isEmpty()) - return; - final ArrayList messageItems = new ArrayList(); - Cursor cursor = MessageTable.getInstance().list(account, user); - try { - if (cursor.moveToFirst()) { - do { - MessageItem messageItem = createMessageItem(cursor); - if (historyIds.contains(messageItem.getId())) - messageItems.add(messageItem); - } while (cursor.moveToNext()); - } - } finally { - cursor.close(); - } - historyIds.clear(); - Application.getInstance().runOnUiThread(new Runnable() { - @Override - public void run() { - addMessages(messageItems); - } - }); - } - - /** - * Updates chat with messages received from server side message archive. - * Replaces local copies with the same tag and offline messages. - * - * @param tag - * @param items - * @param replication - * Whether all message without tag (not received from server - * side) should be removed. - * @return Number of new messages. - */ - public int onMessageDownloaded(String tag, Collection items, - boolean replication) { - int previous = messages.size(); - Iterator iterator = messages.iterator(); - while (iterator.hasNext()) { - MessageItem messageItem = iterator.next(); - if (messageItem.getAction() == null - && !messageItem.isError() - && messageItem.isSent() - && ((replication && messageItem.getTag() == null) - || messageItem.isOffline() || tag - .equals(messageItem.getTag()))) - iterator.remove(); - } - messages.addAll(items); - sort(); - MessageManager.getInstance().onChatChanged(account, user, false); - return Math.max(0, messages.size() - previous); - } - - public boolean isActive() { - return active; - } - - void openChat() { - active = true; - trackStatus = true; - } - - void closeChat() { - active = false; - firstNotification = true; - } - - boolean isStatusTrackingEnabled() { - return trackStatus; - } - - /** - * @return Target address for sending message. - */ - public abstract String getTo(); - - /** - * @return Message type to be assigned. - */ - public abstract Type getType(); - - /** - * @return Whether user never received notifications from this chat. And - * mark as received. - */ - public boolean getFirstNotification() { - boolean result = firstNotification; - firstNotification = false; - return result; - } - - void requestToLoadLocalHistory() { - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - loadHistory(); - } - }); - } - - Collection getMessages() { - return Collections.unmodifiableCollection(messages); - } - - /** - * @return Whether user should be notified about incoming messages in chat. - */ - protected boolean notifyAboutMessage() { - return SettingsManager.eventsMessage() != SettingsManager.EventsMessage.none; - } - - /** - * @param text - * @return New message instance. - */ - abstract protected MessageItem newMessage(String text); - - /** - * Creates new action. - * - * @param resource - * can be null. - * @param text - * can be null. - * @param action - */ - public void newAction(String resource, String text, ChatAction action) { - newMessage(resource, text, action, null, true, false, false, false, - true); - } - - /** - * Creates new message. - * - * Any parameter can be null (except boolean values). - * - * @param resource - * Contact's resource or nick in conference. - * @param text - * message. - * @param action - * Informational message. - * @param delayTimestamp - * Time when incoming message was sent or outgoing was created. - * @param incoming - * Incoming message. - * @param notify - * Notify user about this message when appropriated. - * @param unencrypted - * Whether not encrypted message in OTR chat was received. - * @param offline - * Whether message was received from server side offline storage. - * @param record - * Whether record server side is enabled. - * @return - */ - protected MessageItem newMessage(String resource, String text, - ChatAction action, Date delayTimestamp, boolean incoming, - boolean notify, boolean unencrypted, boolean offline, boolean record) { - boolean save; - boolean visible = MessageManager.getInstance().isVisibleChat(this); - boolean read = incoming ? visible : true; - boolean send = incoming; - if (action == null && text == null) - throw new IllegalArgumentException(); - if (resource == null) - resource = ""; - if (text == null) - text = ""; - if (action != null) { - read = true; - send = true; - save = false; - } else { - ArchiveMode archiveMode = AccountManager.getInstance() - .getArchiveMode(account); - if (archiveMode == ArchiveMode.dontStore) - save = false; - else - save = archiveMode.saveLocally() || !send - || (!read && archiveMode == ArchiveMode.unreadOnly); - if (save) - save = ChatManager.getInstance().isSaveMessages(account, user); - } - if (save - && (unencrypted || (!SettingsManager.securityOtrHistory() && OTRManager - .getInstance().getSecurityLevel(account, user) != SecurityLevel.plain))) - save = false; - Date timestamp = new Date(); - if (notify || !incoming) - openChat(); - if (!incoming) - notify = false; - if (notify && !notifyAboutMessage()) - notify = false; - boolean showTicker = notify; - if (visible && showTicker) { - notify = false; - if (!ChatManager.getInstance().isNotifyVisible(account, user)) - showTicker = false; - } - MessageItem messageItem = new MessageItem(this, record ? null - : NO_RECORD_TAG, resource, text, action, timestamp, - delayTimestamp, incoming, read, send, false, incoming, - unencrypted, offline); - messages.add(messageItem); - updateSendQuery(messageItem); - sort(); - if (save) - requestToWriteMessage(messageItem, resource, text, action, - timestamp, delayTimestamp, incoming, read, send); - if (showTicker) - NotificationManager.getInstance().onMessageNotification( - messageItem, notify); - MessageManager.getInstance().onChatChanged(account, user, incoming); - return messageItem; - } - - private void requestToWriteMessage(final MessageItem messageItem, - final String resource, final String text, final ChatAction action, - final Date timestamp, final Date delayTimestamp, - final boolean incoming, final boolean read, final boolean sent) { - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - long id = MessageTable.getInstance().add(account, user, null, - resource, text, action, timestamp, delayTimestamp, - incoming, read, sent, false); - messageItem.setId(id); - } - }); - } - - private void updateSendQuery(MessageItem messageItem) { - if (!messageItem.isSent()) - sendQuery.add(messageItem); - } - - /** - * Sorts messages and update last text and time. - */ - private void sort() { - Collections.sort(messages); - for (int index = messages.size() - 1; index >= 0; index--) { - MessageItem messageItem = messages.get(index); - if (messageItem.getAction() == null) { - lastText = messageItem.isIncoming() ? messageItem.getText() - : ""; - lastTime = messageItem.getTimestamp(); - return; - } - } - } - - void removeMessage(MessageItem messageItem) { - messages.remove(messageItem); - sendQuery.remove(messageItem); - final ArrayList messageItems = new ArrayList(); - messageItems.add(messageItem); - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - MessageTable.getInstance().removeMessages( - MessageManager.getMessageIds(messageItems, true)); - } - }); - } - - void removeAllMessages() { - final ArrayList messageItems = new ArrayList( - messages); - lastText = ""; - messages.clear(); - sendQuery.clear(); - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - MessageTable.getInstance().removeMessages( - MessageManager.getMessageIds(messageItems, true)); - MessageTable.getInstance().removeMessages(historyIds); - historyIds.clear(); - } - }); - } - - /** - * @param bareAddress - * bareAddress of the user. - * @param user - * full jid. - * @return Whether chat accepts packets from specified user. - */ - boolean accept(String bareAddress, String user) { - return this.user.equals(bareAddress); - } - - /** - * Requests to send all not sent messages. - */ - public void sendMessages() { - sendQueue(null); - } - - /** - * @return Whether chat can send messages. - */ - protected boolean canSendMessage() { - return !sendQuery.isEmpty(); - } - - /** - * @return Last incoming message's text. Empty string if last message is - * outgoing. - */ - protected String getLastText() { - return lastText; - } - - /** - * @return Time of last message in chat. Can be null. - */ - public Date getLastTime() { - return lastTime; - } - - /** - * @param body - * @return New message packet to be sent. - */ - public Message createMessagePacket(String body) { - Message message = new Message(); - message.setTo(getTo()); - message.setType(getType()); - message.setBody(body); - message.setThread(threadId); - return message; - } - - /** - * Prepare text to be send. - * - * @param text - * @return null if text shouldn't be send. - */ - protected String prepareText(String text) { - return text; - } - - /** - * Requests to send messages from queue. - * - * @param intent - * can be null. - */ - protected void sendQueue(MessageItem intent) { - if (!canSendMessage()) - return; - final ArrayList sentMessages = new ArrayList(); - final ArrayList removeMessages = new ArrayList(); - for (final MessageItem messageItem : sendQuery) { - String text = prepareText(messageItem.getText()); - if (text == null) { - messageItem.markAsError(); - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - if (messageItem.getId() != null) - MessageTable.getInstance().markAsError( - messageItem.getId()); - } - }); - } else { - Message message = createMessagePacket(text); - messageItem.setPacketID(message.getPacketID()); - ChatStateManager.getInstance().updateOutgoingMessage(this, - message); - ReceiptManager.getInstance().updateOutgoingMessage(this, - message, messageItem); - if (messageItem != intent) - message.addExtension(new DelayInformation(messageItem - .getTimestamp())); - try { - ConnectionManager.getInstance() - .sendPacket(account, message); - } catch (NetworkException e) { - break; - } - } - if (MessageArchiveManager.getInstance().getSaveMode(account, user, - threadId) == SaveMode.fls) - messageItem.setTag(NO_RECORD_TAG); - if (messageItem != intent) { - messageItem.setSentTimeStamp(new Date()); - Collections.sort(messages); - } - messageItem.markAsSent(); - if (AccountManager.getInstance() - .getArchiveMode(messageItem.getChat().getAccount()) - .saveLocally()) - sentMessages.add(messageItem); - else - removeMessages.add(messageItem); - } - sendQuery.removeAll(sentMessages); - sendQuery.removeAll(removeMessages); - MessageManager.getInstance().onChatChanged(account, user, false); - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - Collection sentIds = MessageManager.getMessageIds( - sentMessages, false); - Collection removeIds = MessageManager.getMessageIds( - removeMessages, true); - MessageTable.getInstance().markAsSent(sentIds); - MessageTable.getInstance().removeMessages(removeIds); - } - }); - } - - public String getThreadId() { - return threadId; - } - - /** - * Update thread id with new value. - * - * @param threadId - * null if current value shouldn't be changed. - */ - protected void updateThreadId(String threadId) { - if (threadId == null) - return; - this.threadId = threadId; - } - - /** - * Returns number of messages to be loaded from server side archive to be - * displayed. Should be called before notification messages will be removed. - * - * @return - */ - public int getRequiredMessageCount() { - int count = PRELOADED_MESSAGES - + NotificationManager.getInstance() - .getNotificationMessageCount(account, user); - Iterator iterator = messages.iterator(); - while (iterator.hasNext()) - if (iterator.next().isIncoming()) { - count -= 1; - if (count <= 0) - return 0; - } - return count; - } - - /** - * Processes incoming packet. - * - * @param bareAddress - * @param packet - * @return Whether packet was directed to this chat. - */ - protected boolean onPacket(String bareAddress, Packet packet) { - return accept(bareAddress, packet.getFrom()); - } - - /** - * Connection complete. - */ - protected void onComplete() { - } - - /** - * Disconnection occured. - */ - protected void onDisconnect() { - } - -} + /** + * Message tag used when server side record is disable. + */ + private static final String NO_RECORD_TAG = "com.xabber.android.data.message.NO_RECORD_TAG"; + + /** + * Number of messages from history to be shown for context purpose. + */ + private static final int PRELOADED_MESSAGES = 3; + /** + * Ids of messages not loaded in to the memory. + *

+ * MUST BE ACCESSED FROM BACKGROUND THREAD ONLY. + */ + protected final Collection historyIds; + /** + * Sorted list of messages in this chat. + */ + protected final List messages; + /** + * List of messages to be sent. + */ + protected final Collection sendQuery; + /** + * Whether chat is open and should be displayed as active chat. + */ + protected boolean active; + /** + * Whether changes in status should be record. + */ + protected boolean trackStatus; + /** + * Whether user never received notifications from this chat. + */ + protected boolean firstNotification; + /** + * Last incoming message's text. + */ + protected String lastText; + /** + * Last message's time. + */ + protected Date lastTime; + protected Date creationTime = new Date(); + /** + * Current thread id. + */ + private String threadId; + private boolean isLastMessageIncoming; + + protected AbstractChat(final String account, final String user) { + super(account, user); + threadId = StringUtils.randomString(12); + active = false; + trackStatus = false; + firstNotification = true; + lastText = ""; + lastTime = null; + historyIds = new ArrayList(); + messages = new ArrayList(); + sendQuery = new ArrayList(); + updateCreationTime(); + + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + loadMessages(); + } + }); + } + + /** + * Load recent messages from local history. + *

+ * CALL THIS METHOD FROM BACKGROUND THREAD ONLY. + */ + private void loadMessages() { + final ArrayList messageItems = new ArrayList(); + LinkedList linkedList = new LinkedList(); + boolean started = false; + Cursor cursor = MessageTable.getInstance().list(account, user); + try { + if (cursor.moveToFirst()) { + do { + MessageItem messageItem = createMessageItem(cursor); + if (started) { + messageItems.add(messageItem); + continue; + } + linkedList.addLast(messageItem); + if (messageItem.isRead() && messageItem.isSent()) { + if (linkedList.size() <= PRELOADED_MESSAGES) + continue; + messageItem = linkedList.removeFirst(); + historyIds.add(messageItem.getId()); + continue; + } + started = true; + messageItems.addAll(linkedList); + } while (cursor.moveToNext()); + } + } finally { + cursor.close(); + } + if (!started) + messageItems.addAll(linkedList); + Application.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + for (MessageItem messageItem : messageItems) + updateSendQuery(messageItem); + addMessages(messageItems); + } + }); + } + + /** + * Update existing message list with loaded. + * + * @param messageItems + */ + private void addMessages(Collection messageItems) { + messages.addAll(messageItems); + sort(); + MessageManager.getInstance().onChatChanged(account, user, false); + } + + /** + * @param cursor + * @return New message item. + */ + private MessageItem createMessageItem(Cursor cursor) { + MessageItem messageItem = new MessageItem(this, + MessageTable.getTag(cursor), MessageTable.getResource(cursor), + MessageTable.getText(cursor), MessageTable.getAction(cursor), + MessageTable.getTimeStamp(cursor), + MessageTable.getDelayTimeStamp(cursor), + MessageTable.isIncoming(cursor), MessageTable.isRead(cursor), + MessageTable.isSent(cursor), MessageTable.hasError(cursor), + true, false, false); + messageItem.setId(MessageTable.getId(cursor)); + return messageItem; + } + + /** + * Load all message from local history. + *

+ * CALL THIS METHOD FROM BACKGROUND THREAD ONLY. + */ + private void loadHistory() { + if (historyIds.isEmpty()) + return; + final ArrayList messageItems = new ArrayList(); + Cursor cursor = MessageTable.getInstance().list(account, user); + try { + if (cursor.moveToFirst()) { + do { + MessageItem messageItem = createMessageItem(cursor); + if (historyIds.contains(messageItem.getId())) + messageItems.add(messageItem); + } while (cursor.moveToNext()); + } + } finally { + cursor.close(); + } + historyIds.clear(); + Application.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + addMessages(messageItems); + } + }); + } + + /** + * Updates chat with messages received from server side message archive. + * Replaces local copies with the same tag and offline messages. + * + * @param tag + * @param items + * @param replication Whether all message without tag (not received from server + * side) should be removed. + * @return Number of new messages. + */ + public int onMessageDownloaded(String tag, Collection items, + boolean replication) { + int previous = messages.size(); + Iterator iterator = messages.iterator(); + while (iterator.hasNext()) { + MessageItem messageItem = iterator.next(); + if (messageItem.getAction() == null + && !messageItem.isError() + && messageItem.isSent() + && ((replication && messageItem.getTag() == null) + || messageItem.isOffline() || tag + .equals(messageItem.getTag()))) + iterator.remove(); + } + messages.addAll(items); + sort(); + MessageManager.getInstance().onChatChanged(account, user, false); + return Math.max(0, messages.size() - previous); + } + + public boolean isActive() { + return active; + } + + void openChat() { + if (!active) { + updateCreationTime(); + } + + active = true; + trackStatus = true; + } + + void closeChat() { + active = false; + firstNotification = true; + } + + boolean isStatusTrackingEnabled() { + return trackStatus; + } + + /** + * @return Target address for sending message. + */ + public abstract String getTo(); + + /** + * @return Message type to be assigned. + */ + public abstract Type getType(); + + /** + * @return Whether user never received notifications from this chat. And + * mark as received. + */ + public boolean getFirstNotification() { + boolean result = firstNotification; + firstNotification = false; + return result; + } + + void requestToLoadLocalHistory() { + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + loadHistory(); + } + }); + } + + Collection getMessages() { + return Collections.unmodifiableCollection(messages); + } + + /** + * @return Whether user should be notified about incoming messages in chat. + */ + protected boolean notifyAboutMessage() { + return SettingsManager.eventsMessage() != SettingsManager.EventsMessage.none; + } + + /** + * @param text + * @return New message instance. + */ + abstract protected MessageItem newMessage(String text); + + /** + * Creates new action. + * + * @param resource can be null. + * @param text can be null. + * @param action + */ + public void newAction(String resource, String text, ChatAction action) { + newMessage(resource, text, action, null, true, false, false, false, true); + } + + /** + * Creates new message. + *

+ * Any parameter can be null (except boolean values). + * + * @param resource Contact's resource or nick in conference. + * @param text message. + * @param action Informational message. + * @param delayTimestamp Time when incoming message was sent or outgoing was created. + * @param incoming Incoming message. + * @param notify Notify user about this message when appropriated. + * @param unencrypted Whether not encrypted message in OTR chat was received. + * @param offline Whether message was received from server side offline storage. + * @param record Whether record server side is enabled. + * @return + */ + protected MessageItem newMessage(String resource, String text, + ChatAction action, Date delayTimestamp, boolean incoming, + boolean notify, boolean unencrypted, boolean offline, boolean record) { + boolean save; + boolean visible = MessageManager.getInstance().isVisibleChat(this); + boolean read = incoming ? visible : true; + boolean send = incoming; + if (action == null && text == null) + throw new IllegalArgumentException(); + if (resource == null) + resource = ""; + if (text == null) + text = ""; + if (action != null) { + read = true; + send = true; + save = false; + } else { + ArchiveMode archiveMode = AccountManager.getInstance() + .getArchiveMode(account); + if (archiveMode == ArchiveMode.dontStore) + save = false; + else + save = archiveMode.saveLocally() || !send + || (!read && archiveMode == ArchiveMode.unreadOnly); + if (save) + save = ChatManager.getInstance().isSaveMessages(account, user); + } + if (save + && (unencrypted || (!SettingsManager.securityOtrHistory() && OTRManager + .getInstance().getSecurityLevel(account, user) != SecurityLevel.plain))) + save = false; + Date timestamp = new Date(); + + if (text.trim().isEmpty()) { + notify = false; + } + + if (notify || !incoming) + openChat(); + if (!incoming) + notify = false; + + MessageItem messageItem = new MessageItem(this, record ? null + : NO_RECORD_TAG, resource, text, action, timestamp, + delayTimestamp, incoming, read, send, false, incoming, + unencrypted, offline); + messages.add(messageItem); + updateSendQuery(messageItem); + sort(); + if (save) + requestToWriteMessage(messageItem, resource, text, action, + timestamp, delayTimestamp, incoming, read, send); + + if (notify && notifyAboutMessage()) { + if (visible) { + if (ChatManager.getInstance().isNotifyVisible(account, user)) { + NotificationManager.getInstance().onCurrentChatMessageNotification(messageItem); + } + } else { + NotificationManager.getInstance().onMessageNotification(messageItem); + } + } + + MessageManager.getInstance().onChatChanged(account, user, incoming); + return messageItem; + } + + private void requestToWriteMessage(final MessageItem messageItem, + final String resource, final String text, final ChatAction action, + final Date timestamp, final Date delayTimestamp, + final boolean incoming, final boolean read, final boolean sent) { + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + long id = MessageTable.getInstance().add(account, user, null, + resource, text, action, timestamp, delayTimestamp, + incoming, read, sent, false); + messageItem.setId(id); + } + }); + } + + private void updateSendQuery(MessageItem messageItem) { + if (!messageItem.isSent()) + sendQuery.add(messageItem); + } + + /** + * Sorts messages and update last text and time. + */ + private void sort() { + Collections.sort(messages); + for (int index = messages.size() - 1; index >= 0; index--) { + MessageItem messageItem = messages.get(index); + if (messageItem.getAction() == null) { + lastText = messageItem.getText(); + lastTime = messageItem.getTimestamp(); + isLastMessageIncoming = messageItem.isIncoming(); + return; + } + } + } + + void removeMessage(MessageItem messageItem) { + messages.remove(messageItem); + sendQuery.remove(messageItem); + final ArrayList messageItems = new ArrayList(); + messageItems.add(messageItem); + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + MessageTable.getInstance().removeMessages(MessageManager.getMessageIds(messageItems, true)); + } + }); + } + + void removeAllMessages() { + final ArrayList messageItems = new ArrayList( + messages); + lastText = ""; + messages.clear(); + sendQuery.clear(); + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + MessageTable.getInstance().removeMessages(MessageManager.getMessageIds(messageItems, true)); + MessageTable.getInstance().removeMessages(historyIds); + historyIds.clear(); + } + }); + } + + /** + * @param bareAddress bareAddress of the user. + * @param user full jid. + * @return Whether chat accepts packets from specified user. + */ + boolean accept(String bareAddress, String user) { + return this.user.equals(bareAddress); + } + + /** + * Requests to send all not sent messages. + */ + public void sendMessages() { + sendQueue(null); + } + + /** + * @return Whether chat can send messages. + */ + protected boolean canSendMessage() { + return !sendQuery.isEmpty(); + } + + /** + * @return Last incoming message's text. Empty string if last message is + * outgoing. + */ + public String getLastText() { + return lastText; + } + + /** + * @return Time of last message in chat. Can be null. + */ + public Date getLastTime() { + return lastTime; + } + + /** + * @param body + * @return New message packet to be sent. + */ + public Message createMessagePacket(String body) { + Message message = new Message(); + message.setTo(getTo()); + message.setType(getType()); + message.setBody(body); + message.setThread(threadId); + return message; + } + + /** + * Prepare text to be send. + * + * @param text + * @return null if text shouldn't be send. + */ + protected String prepareText(String text) { + return text; + } + + /** + * Requests to send messages from queue. + * + * @param intent can be null. + */ + protected void sendQueue(MessageItem intent) { + if (!canSendMessage()) + return; + final ArrayList sentMessages = new ArrayList(); + final ArrayList removeMessages = new ArrayList(); + for (final MessageItem messageItem : sendQuery) { + String text = prepareText(messageItem.getText()); + if (text == null) { + messageItem.markAsError(); + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + if (messageItem.getId() != null) + MessageTable.getInstance().markAsError( + messageItem.getId()); + } + }); + } else { + Message message = createMessagePacket(text); + messageItem.setPacketID(message.getPacketID()); + ChatStateManager.getInstance().updateOutgoingMessage(this, + message); + ReceiptManager.getInstance().updateOutgoingMessage(this, + message, messageItem); + CarbonManager.getInstance().updateOutgoingMessage(this, + message, messageItem); + if (messageItem != intent) + message.addExtension(new DelayInformation(messageItem + .getTimestamp())); + try { + ConnectionManager.getInstance() + .sendPacket(account, message); + } catch (NetworkException e) { + break; + } + } + if (MessageArchiveManager.getInstance().getSaveMode(account, user, + threadId) == SaveMode.fls) + messageItem.setTag(NO_RECORD_TAG); + if (messageItem != intent) { + messageItem.setSentTimeStamp(new Date()); + Collections.sort(messages); + } + messageItem.markAsSent(); + if (AccountManager.getInstance() + .getArchiveMode(messageItem.getChat().getAccount()) + .saveLocally()) + sentMessages.add(messageItem); + else + removeMessages.add(messageItem); + } + sendQuery.removeAll(sentMessages); + sendQuery.removeAll(removeMessages); + MessageManager.getInstance().onChatChanged(account, user, false); + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + Collection sentIds = MessageManager.getMessageIds( + sentMessages, false); + Collection removeIds = MessageManager.getMessageIds( + removeMessages, true); + MessageTable.getInstance().markAsSent(sentIds); + MessageTable.getInstance().removeMessages(removeIds); + } + }); + } + + public String getThreadId() { + return threadId; + } + + /** + * Update thread id with new value. + * + * @param threadId null if current value shouldn't be changed. + */ + protected void updateThreadId(String threadId) { + if (threadId == null) + return; + this.threadId = threadId; + } + + /** + * Returns number of messages to be loaded from server side archive to be + * displayed. Should be called before notification messages will be removed. + * + * @return + */ + public int getRequiredMessageCount() { + int count = PRELOADED_MESSAGES + + NotificationManager.getInstance() + .getNotificationMessageCount(account, user); + for (MessageItem message : messages) + if (message.isIncoming()) { + count -= 1; + if (count <= 0) + return 0; + } + return count; + } + + /** + * Processes incoming packet. + * + * @param bareAddress + * @param packet + * @return Whether packet was directed to this chat. + */ + protected boolean onPacket(String bareAddress, Packet packet) { + return accept(bareAddress, packet.getFrom()); + } + + /** + * Connection complete. + */ + protected void onComplete() { + } + + /** + * Disconnection occured. + */ + protected void onDisconnect() { + } + + public Date getCreationTime() { + return creationTime; + } + + public void updateCreationTime() { + creationTime.setTime(System.currentTimeMillis()); + } + + public boolean isLastMessageIncoming() { + return isLastMessageIncoming; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/data/message/ChatAction.java b/app/src/main/java/com/xabber/android/data/message/ChatAction.java index 44e459ee10..bcc9cc4888 100644 --- a/app/src/main/java/com/xabber/android/data/message/ChatAction.java +++ b/app/src/main/java/com/xabber/android/data/message/ChatAction.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,301 +16,297 @@ import android.content.Context; +import com.xabber.android.R; import com.xabber.android.data.account.StatusMode; -import com.xabber.androiddev.R; /** * Action in chat. - * + * * @author alexander.ivanov - * */ public enum ChatAction { - /** - * Contact becomes available. - */ - available, - - /** - * Contact becomes free for chat. - */ - chat, - - /** - * Contact go away. - */ - away, - - /** - * Contact go away for an extended period of time. - */ - xa, - - /** - * Contact ask to do not disturb. - */ - dnd, - - /** - * Contact becomes unavailable. - */ - unavailable, - - /** - * Contact changes status text. - */ - status, - - /** - * User joins room. - */ - join, - - /** - * User leaves room. - */ - leave, - - /** - * User was kicked. - */ - kick, - - /** - * User was banned. - */ - ban, - - /** - * User changes nickname. - */ - nickname, - - /** - * You have join room. - */ - complete, - - /** - * Invitation to the room was sent. - */ - invite_sent, - - /** - * Invitation was not received. - */ - invite_error, - - /** - * Subject of the room has been changed. - */ - subject, - - /** - * Chat becomes encrypted with verified contact. - */ - otr_verified, - - /** - * Chat becomes encrypted. - */ - otr_encryption, - - /** - * Chat becomes not encrypted. - */ - otr_plain, - - /** - * Chat was finished by another entity. - */ - otr_finish, - - /** - * OTR error. - */ - otr_error, - - /** - * Unreadable OTR message received. - */ - otr_unreadable, - - /** - * Request to send message to the finished session was blocked. - */ - otr_finished_session, - - /** - * Cheat occurred while SMP. - */ - otr_smp_cheated, - - /** - * Error occurred while SMP. - */ - otr_smp_failed, - - /** - * SMP verification for the shared secret or your question has been passed. - */ - otr_smp_verified, - - /** - * OTR verification for your response has been passed. - */ - otr_smp_not_approved, - - /** - * OTR session don't passes verification. - */ - otr_smp_unverified, - - /** - * Call attention. - */ - attention_called, - - /** - * Request attention. - */ - attention_requested; - - public static ChatAction getChatAction(StatusMode statusMode) { - if (statusMode == StatusMode.unavailable) - return ChatAction.unavailable; - else if (statusMode == StatusMode.available) - return ChatAction.available; - else if (statusMode == StatusMode.away) - return ChatAction.away; - else if (statusMode == StatusMode.chat) - return ChatAction.chat; - else if (statusMode == StatusMode.dnd) - return ChatAction.dnd; - else if (statusMode == StatusMode.xa) - return ChatAction.xa; - else - throw new IllegalStateException(); - } - - /** - * @param name - * @return Chat action by name. null if action is empty (usual - * message) or action is unknown. - * - */ - public static ChatAction getChatAction(String name) { - for (ChatAction messageAction : ChatAction.values()) - if (messageAction.name().equals(name)) - return messageAction; - return null; - } - - /** - * @param text - * @return String to be added to the status mode change action. - */ - private static String getOptionalText(String text) { - if ("".equals(text)) - return ""; - else - return " (" + text + ")"; - } - - /** - * @param context - * @param name - * contact's name. - * @param text - * additional text depend on action. - * @return Text representation for the action. - */ - public String getText(Context context, String name, String text) { - if (this == ChatAction.available) - return context.getString(R.string.action_status_available, name) - + getOptionalText(text); - else if (this == ChatAction.away) - return context.getString(R.string.action_status_away, name) - + getOptionalText(text); - else if (this == ChatAction.chat) - return context.getString(R.string.action_status_chat, name) - + getOptionalText(text); - else if (this == ChatAction.dnd) - return context.getString(R.string.action_status_dnd, name) - + getOptionalText(text); - else if (this == ChatAction.unavailable) - return context.getString(R.string.action_status_unavailable, name) - + getOptionalText(text); - else if (this == ChatAction.xa) - return context.getString(R.string.action_status_xa, name) - + getOptionalText(text); - else if (this == ChatAction.status && "".equals(text)) - return context.getString(R.string.action_status_text_none, name); - else if (this == ChatAction.status) - return context.getString(R.string.action_status_text, name, text); - else if (this == ChatAction.join) - return context.getString(R.string.action_join, name); - else if (this == ChatAction.kick && "".equals(text)) - return context.getString(R.string.action_kick, name); - else if (this == ChatAction.kick) - return context.getString(R.string.action_kick_by, name, text); - else if (this == ChatAction.leave) - return context.getString(R.string.action_leave, name); - else if (this == ChatAction.ban && "".equals(text)) - return context.getString(R.string.action_ban, name); - else if (this == ChatAction.ban) - return context.getString(R.string.action_ban_by, name, text); - else if (this == ChatAction.nickname) - return context.getString(R.string.action_nickname, name, text); - else if (this == ChatAction.complete) - return context.getString(R.string.action_join_complete, name); - else if (this == ChatAction.invite_sent) - return context.getString(R.string.action_invite_sent, text); - else if (this == ChatAction.invite_error) - return context.getString(R.string.action_invite_error, text); - else if (this == ChatAction.subject) - return context.getString(R.string.action_subject, name, text); - else if (this == ChatAction.otr_verified) - return context.getString(R.string.action_otr_verified); - else if (this == ChatAction.otr_encryption) - return context.getString(R.string.action_otr_encryption); - else if (this == ChatAction.otr_plain) - return context.getString(R.string.action_otr_plain); - else if (this == ChatAction.otr_finish) - return context.getString(R.string.action_otr_finish); - else if (this == ChatAction.otr_error) - return context.getString(R.string.action_otr_error, text); - else if (this == ChatAction.otr_unreadable) - return context.getString(R.string.action_otr_unreadable); - else if (this == ChatAction.otr_finished_session) - return context.getString(R.string.action_otr_finished_session); - else if (this == ChatAction.otr_smp_cheated) - return context.getString(R.string.action_otr_smp_cheated); - else if (this == ChatAction.otr_smp_failed) - return context.getString(R.string.action_otr_smp_failed); - else if (this == ChatAction.otr_smp_not_approved) - return context.getString(R.string.action_otr_smp_not_approved); - else if (this == ChatAction.otr_smp_verified) - return context.getString(R.string.action_otr_smp_verified); - else if (this == ChatAction.otr_smp_unverified) - return context.getString(R.string.action_otr_smp_unverified); - else if (this == ChatAction.attention_called) - return context.getString(R.string.action_attention_called); - else if (this == ChatAction.attention_requested) - return context.getString(R.string.action_attention_requested); - else - throw new IllegalStateException(); - } - - /** - * @return Whether action is status change. - */ - public boolean isStatusChage() { - return this == ChatAction.available || this == ChatAction.away - || this == ChatAction.chat || this == ChatAction.dnd - || this == ChatAction.unavailable || this == ChatAction.xa - || this == ChatAction.status; - } + /** + * Contact becomes available. + */ + available, + + /** + * Contact becomes free for chat. + */ + chat, + + /** + * Contact go away. + */ + away, + + /** + * Contact go away for an extended period of time. + */ + xa, + + /** + * Contact ask to do not disturb. + */ + dnd, + + /** + * Contact becomes unavailable. + */ + unavailable, + + /** + * Contact changes status text. + */ + status, + + /** + * User joins room. + */ + join, + + /** + * User leaves room. + */ + leave, + + /** + * User was kicked. + */ + kick, + + /** + * User was banned. + */ + ban, + + /** + * User changes nickname. + */ + nickname, + + /** + * You have join room. + */ + complete, + + /** + * Invitation to the room was sent. + */ + invite_sent, + + /** + * Invitation was not received. + */ + invite_error, + + /** + * Subject of the room has been changed. + */ + subject, + + /** + * Chat becomes encrypted with verified contact. + */ + otr_verified, + + /** + * Chat becomes encrypted. + */ + otr_encryption, + + /** + * Chat becomes not encrypted. + */ + otr_plain, + + /** + * Chat was finished by another entity. + */ + otr_finish, + + /** + * OTR error. + */ + otr_error, + + /** + * Unreadable OTR message received. + */ + otr_unreadable, + + /** + * Request to send message to the finished session was blocked. + */ + otr_finished_session, + + /** + * Cheat occurred while SMP. + */ + otr_smp_cheated, + + /** + * Error occurred while SMP. + */ + otr_smp_failed, + + /** + * SMP verification for the shared secret or your question has been passed. + */ + otr_smp_verified, + + /** + * OTR verification for your response has been passed. + */ + otr_smp_not_approved, + + /** + * OTR session don't passes verification. + */ + otr_smp_unverified, + + /** + * Call attention. + */ + attention_called, + + /** + * Request attention. + */ + attention_requested; + + public static ChatAction getChatAction(StatusMode statusMode) { + if (statusMode == StatusMode.unavailable) + return ChatAction.unavailable; + else if (statusMode == StatusMode.available) + return ChatAction.available; + else if (statusMode == StatusMode.away) + return ChatAction.away; + else if (statusMode == StatusMode.chat) + return ChatAction.chat; + else if (statusMode == StatusMode.dnd) + return ChatAction.dnd; + else if (statusMode == StatusMode.xa) + return ChatAction.xa; + else + throw new IllegalStateException(); + } + + /** + * @param name + * @return Chat action by name. null if action is empty (usual + * message) or action is unknown. + */ + public static ChatAction getChatAction(String name) { + for (ChatAction messageAction : ChatAction.values()) + if (messageAction.name().equals(name)) + return messageAction; + return null; + } + + /** + * @param text + * @return String to be added to the status mode change action. + */ + private static String getOptionalText(String text) { + if ("".equals(text)) + return ""; + else + return " (" + text + ")"; + } + + /** + * @param context + * @param name contact's name. + * @param text additional text depend on action. + * @return Text representation for the action. + */ + public String getText(Context context, String name, String text) { + if (this == ChatAction.available) + return context.getString(R.string.action_status_available, name) + + getOptionalText(text); + else if (this == ChatAction.away) + return context.getString(R.string.action_status_away, name) + + getOptionalText(text); + else if (this == ChatAction.chat) + return context.getString(R.string.action_status_chat, name) + + getOptionalText(text); + else if (this == ChatAction.dnd) + return context.getString(R.string.action_status_dnd, name) + + getOptionalText(text); + else if (this == ChatAction.unavailable) + return context.getString(R.string.action_status_unavailable, name) + + getOptionalText(text); + else if (this == ChatAction.xa) + return context.getString(R.string.action_status_xa, name) + + getOptionalText(text); + else if (this == ChatAction.status && "".equals(text)) + return context.getString(R.string.action_status_text_none, name); + else if (this == ChatAction.status) + return context.getString(R.string.action_status_text, name, text); + else if (this == ChatAction.join) + return context.getString(R.string.action_join, name); + else if (this == ChatAction.kick && "".equals(text)) + return context.getString(R.string.action_kick, name); + else if (this == ChatAction.kick) + return context.getString(R.string.action_kick_by, name, text); + else if (this == ChatAction.leave) + return context.getString(R.string.action_leave, name); + else if (this == ChatAction.ban && "".equals(text)) + return context.getString(R.string.action_ban, name); + else if (this == ChatAction.ban) + return context.getString(R.string.action_ban_by, name, text); + else if (this == ChatAction.nickname) + return context.getString(R.string.action_nickname, name, text); + else if (this == ChatAction.complete) + return context.getString(R.string.action_join_complete, name); + else if (this == ChatAction.invite_sent) + return context.getString(R.string.action_invite_sent, text); + else if (this == ChatAction.invite_error) + return context.getString(R.string.action_invite_error, text); + else if (this == ChatAction.subject) + return context.getString(R.string.action_subject, name, text); + else if (this == ChatAction.otr_verified) + return context.getString(R.string.action_otr_verified); + else if (this == ChatAction.otr_encryption) + return context.getString(R.string.action_otr_encryption); + else if (this == ChatAction.otr_plain) + return context.getString(R.string.action_otr_plain); + else if (this == ChatAction.otr_finish) + return context.getString(R.string.action_otr_finish); + else if (this == ChatAction.otr_error) + return context.getString(R.string.action_otr_error, text); + else if (this == ChatAction.otr_unreadable) + return context.getString(R.string.action_otr_unreadable); + else if (this == ChatAction.otr_finished_session) + return context.getString(R.string.action_otr_finished_session); + else if (this == ChatAction.otr_smp_cheated) + return context.getString(R.string.action_otr_smp_cheated); + else if (this == ChatAction.otr_smp_failed) + return context.getString(R.string.action_otr_smp_failed); + else if (this == ChatAction.otr_smp_not_approved) + return context.getString(R.string.action_otr_smp_not_approved); + else if (this == ChatAction.otr_smp_verified) + return context.getString(R.string.action_otr_smp_verified); + else if (this == ChatAction.otr_smp_unverified) + return context.getString(R.string.action_otr_smp_unverified); + else if (this == ChatAction.attention_called) + return context.getString(R.string.action_attention_called); + else if (this == ChatAction.attention_requested) + return context.getString(R.string.action_attention_requested); + else + throw new IllegalStateException(); + } + + /** + * @return Whether action is status change. + */ + public boolean isStatusChage() { + return this == ChatAction.available || this == ChatAction.away + || this == ChatAction.chat || this == ChatAction.dnd + || this == ChatAction.unavailable || this == ChatAction.xa + || this == ChatAction.status; + } } diff --git a/app/src/main/java/com/xabber/android/data/message/ChatContact.java b/app/src/main/java/com/xabber/android/data/message/ChatContact.java index 8a905b1385..a591b56cb7 100644 --- a/app/src/main/java/com/xabber/android/data/message/ChatContact.java +++ b/app/src/main/java/com/xabber/android/data/message/ChatContact.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,28 +20,27 @@ /** * Represent contact outside of roster with opened chat. - * + * * @author alexander.ivanov - * */ public class ChatContact extends AbstractContact { - public ChatContact(String account, String user) { - super(account, user); - } + public ChatContact(String account, String user) { + super(account, user); + } - public ChatContact(AbstractChat abstractChat) { - super(abstractChat.getAccount(), abstractChat.getUser()); - } + public ChatContact(AbstractChat abstractChat) { + super(abstractChat.getAccount(), abstractChat.getUser()); + } - @Override - public boolean isConnected() { - AccountItem accountItem = AccountManager.getInstance().getAccount( - account); - if (accountItem == null) - return false; - else - return accountItem.getState().isConnected(); - } + @Override + public boolean isConnected() { + AccountItem accountItem = AccountManager.getInstance().getAccount( + account); + if (accountItem == null) + return false; + else + return accountItem.getState().isConnected(); + } } diff --git a/app/src/main/java/com/xabber/android/data/message/MessageItem.java b/app/src/main/java/com/xabber/android/data/message/MessageItem.java index f9d0855941..9584b77ceb 100644 --- a/app/src/main/java/com/xabber/android/data/message/MessageItem.java +++ b/app/src/main/java/com/xabber/android/data/message/MessageItem.java @@ -1,243 +1,223 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data.message; -import java.util.Date; - import android.text.Spannable; -import android.text.util.Linkify; +import android.text.SpannableString; -import com.xabber.android.utils.Emoticons; -import com.xabber.xmpp.uri.XMPPUri; +import java.util.Date; /** * Message item. - * + * * @author alexander.ivanov - * */ public class MessageItem implements Comparable { - private final AbstractChat chat; - - /** - * Tag used to identify collection in server side message archive. Equals to - * collection's start attribute. - */ - private String tag; - - /** - * Contact's resource. - */ - private final String resource; - - /** - * Text representation. - */ - private final String text; - - /** - * Cached text populated with smiles and link. - */ - private Spannable spannable; - - /** - * Optional action. If set message represent not an actual message but some - * action in the chat. - */ - private final ChatAction action; - - /** - * Time when message was received or sent by Xabber. - */ - private Date timestamp; - - /** - * Time when message was created. - */ - private Date delayTimestamp; - private final boolean incoming; - private final boolean unencypted; - - /** - * ID in database. - */ - private Long id; - - /** - * Error response received on send request. - */ - private boolean error; - - /** - * Receipt was received for sent message. - */ - private boolean delivered; - - /** - * Message was sent. - */ - private boolean sent; - - /** - * Message was shown to the user. - */ - private boolean read; - - /** - * Message was received from server side offline storage. - */ - private final boolean offline; - - /** - * Outgoing packet id. - */ - private String packetID; - - public MessageItem(AbstractChat chat, String tag, String resource, - String text, ChatAction action, Date timestamp, - Date delayTimestamp, boolean incoming, boolean read, boolean sent, - boolean error, boolean delivered, boolean unencypted, - boolean offline) { - this.chat = chat; - this.tag = tag; - this.resource = resource; - this.text = text; - this.action = action; - this.timestamp = timestamp; - this.delayTimestamp = delayTimestamp; - this.incoming = incoming; - this.read = read; - this.sent = sent; - this.error = error; - this.delivered = delivered; - this.unencypted = unencypted; - this.offline = offline; - this.id = null; - this.packetID = null; - } - - public AbstractChat getChat() { - return chat; - } - - public String getTag() { - return tag; - } - - public void setTag(String tag) { - this.tag = tag; - } - - public String getResource() { - return resource; - } - - public String getText() { - return text; - } - - public Spannable getSpannable() { - if (spannable == null) { - spannable = Emoticons.newSpannable(text); - Linkify.addLinks(this.spannable, Linkify.ALL); - XMPPUri.addLinks(this.spannable); - } - return spannable; - } - - public ChatAction getAction() { - return action; - } - - public Date getTimestamp() { - return timestamp; - } - - public Date getDelayTimestamp() { - return delayTimestamp; - } - - public boolean isIncoming() { - return incoming; - } - - public boolean isError() { - return error; - } - - public boolean isDelivered() { - return delivered; - } - - public boolean isUnencypted() { - return unencypted; - } - - public boolean isOffline() { - return offline; - } - - public boolean isSent() { - return sent; - } - - public boolean isRead() { - return read; - } - - public Long getId() { - return id; - } - - void setId(Long id) { - this.id = id; - } - - public String getPacketID() { - return packetID; - } - - public void setPacketID(String packetID) { - this.packetID = packetID; - } - - void markAsError() { - error = true; - } - - void markAsSent() { - sent = true; - } - - void setSentTimeStamp(Date timestamp) { - this.delayTimestamp = this.timestamp; - this.timestamp = timestamp; - } - - void markAsRead() { - read = true; - } - - void markAsDelivered() { - delivered = true; - } - - @Override - public int compareTo(MessageItem another) { - return timestamp.compareTo(another.timestamp); - } + private final AbstractChat chat; + /** + * Contact's resource. + */ + private final String resource; + /** + * Text representation. + */ + private final String text; + /** + * Optional action. If set message represent not an actual message but some + * action in the chat. + */ + private final ChatAction action; + private final boolean incoming; + private final boolean unencypted; + /** + * Message was received from server side offline storage. + */ + private final boolean offline; + /** + * Tag used to identify collection in server side message archive. Equals to + * collection's start attribute. + */ + private String tag; + /** + * Cached text populated with smiles and link. + */ + private Spannable spannable; + /** + * Time when message was received or sent by Xabber. + */ + private Date timestamp; + /** + * Time when message was created. + */ + private Date delayTimestamp; + /** + * ID in database. + */ + private Long id; + /** + * Error response received on send request. + */ + private boolean error; + /** + * Receipt was received for sent message. + */ + private boolean delivered; + /** + * Message was sent. + */ + private boolean sent; + /** + * Message was shown to the user. + */ + private boolean read; + /** + * Outgoing packet id. + */ + private String packetID; + + public MessageItem(AbstractChat chat, String tag, String resource, + String text, ChatAction action, Date timestamp, + Date delayTimestamp, boolean incoming, boolean read, boolean sent, + boolean error, boolean delivered, boolean unencypted, + boolean offline) { + this.chat = chat; + this.tag = tag; + this.resource = resource; + this.text = text; + this.action = action; + this.timestamp = timestamp; + this.delayTimestamp = delayTimestamp; + this.incoming = incoming; + this.read = read; + this.sent = sent; + this.error = error; + this.delivered = delivered; + this.unencypted = unencypted; + this.offline = offline; + this.id = null; + this.packetID = null; + } + + public AbstractChat getChat() { + return chat; + } + + public String getTag() { + return tag; + } + + public void setTag(String tag) { + this.tag = tag; + } + + public String getResource() { + return resource; + } + + public String getText() { + return text; + } + + public Spannable getSpannable() { + if (spannable == null) { + spannable = new SpannableString(text); + } + return spannable; + } + + public ChatAction getAction() { + return action; + } + + public Date getTimestamp() { + return timestamp; + } + + public Date getDelayTimestamp() { + return delayTimestamp; + } + + public boolean isIncoming() { + return incoming; + } + + public boolean isError() { + return error; + } + + public boolean isDelivered() { + return delivered; + } + + public boolean isUnencypted() { + return unencypted; + } + + public boolean isOffline() { + return offline; + } + + public boolean isSent() { + return sent; + } + + public boolean isRead() { + return read; + } + + public Long getId() { + return id; + } + + void setId(Long id) { + this.id = id; + } + + public String getPacketID() { + return packetID; + } + + public void setPacketID(String packetID) { + this.packetID = packetID; + } + + void markAsError() { + error = true; + } + + void markAsSent() { + sent = true; + } + + void setSentTimeStamp(Date timestamp) { + this.delayTimestamp = this.timestamp; + this.timestamp = timestamp; + } + + void markAsRead() { + read = true; + } + + void markAsDelivered() { + delivered = true; + } + + @Override + public int compareTo(MessageItem another) { + return timestamp.compareTo(another.timestamp); + } } diff --git a/app/src/main/java/com/xabber/android/data/message/MessageManager.java b/app/src/main/java/com/xabber/android/data/message/MessageManager.java index 64874ac8b7..51b4bb53a2 100644 --- a/app/src/main/java/com/xabber/android/data/message/MessageManager.java +++ b/app/src/main/java/com/xabber/android/data/message/MessageManager.java @@ -1,38 +1,23 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data.message; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.HashSet; -import java.util.Set; - -import org.jivesoftware.smack.packet.Message; -import org.jivesoftware.smack.packet.Packet; -import org.jivesoftware.smack.packet.PacketExtension; -import org.jivesoftware.smackx.packet.MUCUser; - import android.database.Cursor; import android.os.Environment; +import com.xabber.android.R; import com.xabber.android.data.Application; import com.xabber.android.data.NetworkException; import com.xabber.android.data.OnLoadListener; @@ -42,6 +27,7 @@ import com.xabber.android.data.account.AccountManager; import com.xabber.android.data.account.ArchiveMode; import com.xabber.android.data.account.OnAccountArchiveModeChangedListener; +import com.xabber.android.data.account.OnAccountDisabledListener; import com.xabber.android.data.account.OnAccountRemovedListener; import com.xabber.android.data.account.StatusMode; import com.xabber.android.data.connection.ConnectionItem; @@ -51,561 +37,642 @@ import com.xabber.android.data.entity.NestedMap; import com.xabber.android.data.extension.archive.MessageArchiveManager; import com.xabber.android.data.extension.muc.RoomChat; -import com.xabber.android.data.roster.OnStatusChangeListener; import com.xabber.android.data.roster.OnRosterReceivedListener; +import com.xabber.android.data.roster.OnStatusChangeListener; import com.xabber.android.data.roster.RosterManager; import com.xabber.android.utils.StringUtils; -import com.xabber.androiddev.R; import com.xabber.xmpp.address.Jid; +import com.xabber.xmpp.carbon.CarbonManager.Direction; import com.xabber.xmpp.delay.Delay; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smack.packet.PacketExtension; +import org.jivesoftware.smackx.packet.MUCUser; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.Set; + /** * Manage chats and its messages. - * + *

* Warning: message processing using chat instances should be changed. - * + * * @author alexander.ivanov - * */ -public class MessageManager implements OnLoadListener, OnPacketListener, - OnDisconnectListener, OnAccountRemovedListener, - OnRosterReceivedListener, OnAccountArchiveModeChangedListener, - OnStatusChangeListener { - - /** - * Registered chats for bareAddresses in accounts. - */ - private final NestedMap chats; - - /** - * Visible chat. - * - * Will be null if there is no one. - */ - private AbstractChat visibleChat; - - private final static MessageManager instance; - - static { - instance = new MessageManager(); - Application.getInstance().addManager(instance); - } - - public static MessageManager getInstance() { - return instance; - } - - private MessageManager() { - chats = new NestedMap(); - } - - @Override - public void onLoad() { - final Set loadChats = new HashSet(); - Cursor cursor; - cursor = MessageTable.getInstance().messagesToSend(); - try { - if (cursor.moveToFirst()) { - do { - loadChats.add(new BaseEntity(MessageTable - .getAccount(cursor), MessageTable.getUser(cursor))); - } while (cursor.moveToNext()); - } - } finally { - cursor.close(); - } - Application.getInstance().runOnUiThread(new Runnable() { - @Override - public void run() { - onLoaded(loadChats); - } - }); - } - - private void onLoaded(Set loadChats) { - for (BaseEntity baseEntity : loadChats) - if (getChat(baseEntity.getAccount(), - Jid.getBareAddress(baseEntity.getUser())) == null) - createChat(baseEntity.getAccount(), baseEntity.getUser()); - } - - /** - * @param account - * @param user - * @return null if there is no such chat. - */ - public AbstractChat getChat(String account, String user) { - return chats.get(account, user); - } - - public Collection getChats() { - return Collections.unmodifiableCollection(chats.values()); - } - - /** - * Creates and adds new regular chat to be managed. - * - * @param account - * @param user - * @return - */ - private RegularChat createChat(String account, String user) { - RegularChat chat = new RegularChat(account, Jid.getBareAddress(user)); - addChat(chat); - return chat; - } - - /** - * Adds chat to be managed. - * - * @param chat - */ - public void addChat(AbstractChat chat) { - if (getChat(chat.getAccount(), chat.getUser()) != null) - throw new IllegalStateException(); - chats.put(chat.getAccount(), chat.getUser(), chat); - } - - /** - * Removes chat from managed. - * - * @param chat - */ - public void removeChat(AbstractChat chat) { - chats.remove(chat.getAccount(), chat.getUser()); - } - - /** - * Sends message. Creates and registers new chat if necessary. - * - * @param account - * @param user - * @param text - */ - public void sendMessage(String account, String user, String text) { - AbstractChat chat = getChat(account, user); - if (chat == null) - chat = createChat(account, user); - MessageItem messageItem = chat.newMessage(text); - chat.sendQueue(messageItem); - } - - /** - * @param account - * @param user - * @return Where there is active chat. - */ - public boolean hasActiveChat(String account, String user) { - AbstractChat chat = getChat(account, user); - if (chat == null) - return false; - return chat.isActive(); - } - - /** - * @return Collection with active chats. - */ - public Collection getActiveChats() { - Collection collection = new ArrayList(); - for (AbstractChat chat : chats.values()) - if (chat.isActive()) - collection.add(chat); - return Collections.unmodifiableCollection(collection); - } - - /** - * Returns existed chat or create new one. - * - * @param account - * @param user - * @return - */ - public AbstractChat getOrCreateChat(String account, String user) { - AbstractChat chat = getChat(account, user); - if (chat == null) - chat = createChat(account, user); - return chat; - } - - /** - * Force open chat (make it active). - * - * @param account - * @param user - */ - public void openChat(String account, String user) { - getOrCreateChat(account, user).openChat(); - } - - /** - * Closes specified chat (make it inactive). - * - * @param account - * @param user - */ - public void closeChat(String account, String user) { - AbstractChat chat = getChat(account, user); - if (chat == null) - return; - chat.closeChat(); - } - - public void requestToLoadLocalHistory(String account, String user) { - AbstractChat chat = getChat(account, user); - if (chat == null) - chat = createChat(account, user); - chat.requestToLoadLocalHistory(); - } - - /** - * @param account - * @param user - * @return Last incoming message's text. Empty string if last message is - * outgoing. - */ - public String getLastText(String account, String user) { - AbstractChat chat = getChat(account, user); - if (chat == null) - return ""; - return chat.getLastText(); - } - - /** - * @param account - * @param user - * @return Time of last message in chat. Can be null. - */ - public Date getLastTime(String account, String user) { - AbstractChat chat = getChat(account, user); - if (chat == null) - return null; - return chat.getLastTime(); - } - - /** - * Sets currently visible chat. - * - * @param account - * @param user - */ - public void setVisibleChat(String account, String user) { - final boolean remove = !AccountManager.getInstance() - .getArchiveMode(account).saveLocally(); - AbstractChat chat = getChat(account, user); - if (chat == null) - chat = createChat(account, user); - else { - // Mark messages as read and them delete from db if necessary. - final ArrayList messageItems = new ArrayList(); - for (MessageItem messageItem : chat.getMessages()) { - if (!messageItem.isRead()) { - messageItem.markAsRead(); - messageItems.add(messageItem); - } - } - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - Collection ids = getMessageIds(messageItems, remove); - if (remove) - MessageTable.getInstance().removeMessages(ids); - else - MessageTable.getInstance().markAsRead(ids); - } - }); - } - visibleChat = chat; - } - - /** - * All chats become invisible. - */ - public void removeVisibleChat() { - visibleChat = null; - } - - /** - * @param chat - * @return Whether specified chat is currently visible. - */ - boolean isVisibleChat(AbstractChat chat) { - return visibleChat == chat; - } - - /** - * Removes all messages from chat. - * - * @param account - * @param user - */ - public void clearHistory(String account, String user) { - AbstractChat chat = getChat(account, user); - if (chat == null) - return; - chat.removeAllMessages(); - onChatChanged(chat.getAccount(), chat.getUser(), false); - } - - /** - * Removes message from history. - * - * @param messageItem - */ - public void removeMessage(MessageItem messageItem) { - AbstractChat chat = messageItem.getChat(); - chat.removeMessage(messageItem); - onChatChanged(chat.getAccount(), chat.getUser(), false); - } - - /** - * @param account - * @param user - * @return List of messages or empty list. - */ - public Collection getMessages(String account, String user) { - AbstractChat chat = getChat(account, user); - if (chat == null) - return Collections.emptyList(); - return chat.getMessages(); - } - - /** - * Called on action settings change. - */ - public void onSettingsChanged() { - ChatsShowStatusChange showStatusChange = SettingsManager - .chatsShowStatusChange(); - Collection changedEntities = new ArrayList(); - for (AbstractChat chat : chats.values()) - if ((chat instanceof RegularChat && showStatusChange != ChatsShowStatusChange.always) - || (chat instanceof RoomChat && showStatusChange == ChatsShowStatusChange.never)) { - // Remove actions with status change. - ArrayList remove = new ArrayList(); - for (MessageItem messageItem : chat.getMessages()) - if (messageItem.getAction() != null - && messageItem.getAction().isStatusChage()) - remove.add(messageItem); - if (remove.isEmpty()) - continue; - for (MessageItem messageItem : remove) - chat.removeMessage(messageItem); - changedEntities.add(chat); - } - RosterManager.getInstance().onContactsChanged(changedEntities); - } - - @Override - public void onAccountArchiveModeChanged(AccountItem accountItem) { - final ArchiveMode archiveMode = AccountManager.getInstance() - .getArchiveMode(accountItem.getAccount()); - if (archiveMode.saveLocally()) - return; - final String account = accountItem.getAccount(); - final ArrayList removeMessageItems = new ArrayList(); - for (AbstractChat chat : chats.getNested(account).values()) - for (MessageItem messageItem : chat.getMessages()) - if (archiveMode == ArchiveMode.dontStore - || ((messageItem.isRead() || archiveMode != ArchiveMode.unreadOnly) && messageItem - .isSent())) - removeMessageItems.add(messageItem); - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - // If message was read or received after removeMessageItems - // was created then it's ID will be not null. DB actions with - // such message will have no effect as if it was removed. - // History ids becomes invalid and will be cleared on next - // history load. - MessageTable.getInstance().removeMessages( - getMessageIds(removeMessageItems, true)); - if (archiveMode == ArchiveMode.dontStore) - MessageTable.getInstance().removeAccount(account); - else if (archiveMode == ArchiveMode.unreadOnly) - MessageTable.getInstance().removeReadAndSent(account); - else - MessageTable.getInstance().removeSent(account); - } - }); - AccountManager.getInstance().onAccountChanged(accountItem.getAccount()); - } - - @Override - public void onPacket(ConnectionItem connection, String bareAddress, - Packet packet) { - if (!(connection instanceof AccountItem)) - return; - String account = ((AccountItem) connection).getAccount(); - if (bareAddress == null) - return; - if (packet instanceof Message - && MessageArchiveManager.getInstance().isModificationsSucceed( - account) - && Delay.isOfflineMessage(Jid.getServer(account), packet)) - // Ignore offline message if modification from server side message - // archive have been received. - return; - final String user = packet.getFrom(); - boolean processed = false; - for (AbstractChat chat : chats.getNested(account).values()) - if (chat.onPacket(bareAddress, packet)) { - processed = true; - break; - } - if (getChat(account, user) != null) - return; - if (!processed && packet instanceof Message) { - final Message message = (Message) packet; - final String body = message.getBody(); - if (body == null) - return; - for (PacketExtension packetExtension : message.getExtensions()) - if (packetExtension instanceof MUCUser) - return; - createChat(account, user).onPacket(bareAddress, packet); - } - } - - @Override - public void onRosterReceived(AccountItem accountItem) { - String account = accountItem.getAccount(); - for (AbstractChat chat : chats.getNested(account).values()) - chat.onComplete(); - } - - @Override - public void onDisconnect(ConnectionItem connection) { - if (!(connection instanceof AccountItem)) - return; - String account = ((AccountItem) connection).getAccount(); - for (AbstractChat chat : chats.getNested(account).values()) - chat.onDisconnect(); - } - - @Override - public void onAccountRemoved(AccountItem accountItem) { - chats.clear(accountItem.getAccount()); - } - - /** - * Export chat to file with specified name. - * - * @param account - * @param user - * @param fileName - * @throws NetworkException - */ - public File exportChat(String account, String user, String fileName) - throws NetworkException { - final File file = new File(Environment.getExternalStorageDirectory(), - fileName); - try { - BufferedWriter out = new BufferedWriter(new FileWriter(file)); - final String titleName = RosterManager.getInstance().getName( - account, user) - + " (" + user + ")"; - out.write(""); - out.write(StringUtils.escapeHtml(titleName)); - out.write(""); - final AbstractChat abstractChat = getChat(account, user); - if (abstractChat != null) { - final boolean isMUC = abstractChat instanceof RoomChat; - final String accountName = AccountManager.getInstance() - .getNickName(account); - final String userName = RosterManager.getInstance().getName( - account, user); - for (MessageItem messageItem : abstractChat.getMessages()) { - if (messageItem.getAction() != null) - continue; - final String name; - if (isMUC) { - name = messageItem.getResource(); - } else { - if (messageItem.isIncoming()) - name = userName; - else - name = accountName; - } - out.write(""); - out.write(StringUtils.escapeHtml(name)); - out.write(" ("); - out.write(StringUtils.getDateTimeText(messageItem - .getTimestamp())); - out.write(")
\n

"); - out.write(StringUtils.escapeHtml(messageItem.getText())); - out.write("


\n"); - } - } - out.write(""); - out.close(); - } catch (IOException e) { - throw new NetworkException(R.string.FILE_NOT_FOUND); - } - return file; - } - - /** - * Notifies registered {@link OnChatChangedListener}. - * - * @param account - * @param user - * @param incoming - */ - public void onChatChanged(final String account, final String user, - final boolean incoming) { - Application.getInstance().runOnUiThread(new Runnable() { - @Override - public void run() { - for (OnChatChangedListener onChatChangedListener : Application - .getInstance().getUIListeners( - OnChatChangedListener.class)) - onChatChangedListener - .onChatChanged(account, user, incoming); - } - }); - } - - /** - * @param messageItems - * @param clearId - * Whether message id must be set to the null. - * @return Collection with ids for specified messages. - */ - static Collection getMessageIds(Collection messageItems, - boolean clearId) { - ArrayList ids = new ArrayList(); - for (MessageItem messageItem : messageItems) { - Long id = messageItem.getId(); - if (id == null) - continue; - ids.add(id); - if (clearId) - messageItem.setId(null); - } - return ids; - } - - private boolean isStatusTrackingEnabled(String account, String bareAddress) { - if (SettingsManager.chatsShowStatusChange() != ChatsShowStatusChange.always) - return false; - AbstractChat abstractChat = getChat(account, bareAddress); - return abstractChat != null && abstractChat instanceof RegularChat - && abstractChat.isStatusTrackingEnabled(); - } - - @Override - public void onStatusChanged(String account, String bareAddress, - String resource, String statusText) { - if (isStatusTrackingEnabled(account, bareAddress)) - getChat(account, bareAddress).newAction(resource, statusText, - ChatAction.status); - } - - @Override - public void onStatusChanged(String account, String bareAddress, - String resource, StatusMode statusMode, String statusText) { - if (isStatusTrackingEnabled(account, bareAddress)) - getChat(account, bareAddress).newAction(resource, statusText, - ChatAction.getChatAction(statusMode)); - } - -} +public class MessageManager implements OnLoadListener, OnPacketListener, OnDisconnectListener, + OnAccountRemovedListener, OnAccountDisabledListener, OnRosterReceivedListener, + OnAccountArchiveModeChangedListener, OnStatusChangeListener { + + private final static MessageManager instance; + + static { + instance = new MessageManager(); + Application.getInstance().addManager(instance); + } + + /** + * Registered chats for bareAddresses in accounts. + */ + private final NestedMap chats; + /** + * Visible chat. + *

+ * Will be null if there is no one. + */ + private AbstractChat visibleChat; + + private MessageManager() { + chats = new NestedMap<>(); + } + + public static MessageManager getInstance() { + return instance; + } + + /** + * @param messageItems + * @param clearId Whether message id must be set to the null. + * @return Collection with ids for specified messages. + */ + static Collection getMessageIds(Collection messageItems, boolean clearId) { + ArrayList ids = new ArrayList<>(); + for (MessageItem messageItem : messageItems) { + Long id = messageItem.getId(); + if (id == null) { + continue; + } + ids.add(id); + if (clearId) { + messageItem.setId(null); + } + } + return ids; + } + + @Override + public void onLoad() { + final Set loadChats = new HashSet(); + Cursor cursor; + cursor = MessageTable.getInstance().messagesToSend(); + try { + if (cursor.moveToFirst()) { + do { + loadChats.add(new BaseEntity(MessageTable.getAccount(cursor), MessageTable.getUser(cursor))); + } while (cursor.moveToNext()); + } + } finally { + cursor.close(); + } + Application.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + onLoaded(loadChats); + } + }); + } + + private void onLoaded(Set loadChats) { + for (BaseEntity baseEntity : loadChats) { + if (getChat(baseEntity.getAccount(), Jid.getBareAddress(baseEntity.getUser())) == null) { + createChat(baseEntity.getAccount(), baseEntity.getUser()); + } + } + } + + /** + * @param account + * @param user + * @return null if there is no such chat. + */ + public AbstractChat getChat(String account, String user) { + return chats.get(account, user); + } + + public Collection getChats() { + return Collections.unmodifiableCollection(chats.values()); + } + + /** + * Creates and adds new regular chat to be managed. + * + * @param account + * @param user + * @return + */ + private RegularChat createChat(String account, String user) { + RegularChat chat = new RegularChat(account, Jid.getBareAddress(user)); + addChat(chat); + return chat; + } + + /** + * Adds chat to be managed. + * + * @param chat + */ + public void addChat(AbstractChat chat) { + if (getChat(chat.getAccount(), chat.getUser()) != null) { + throw new IllegalStateException(); + } + chats.put(chat.getAccount(), chat.getUser(), chat); + } + + /** + * Removes chat from managed. + * + * @param chat + */ + public void removeChat(AbstractChat chat) { + chats.remove(chat.getAccount(), chat.getUser()); + } + + /** + * Sends message. Creates and registers new chat if necessary. + * + * @param account + * @param user + * @param text + */ + public void sendMessage(String account, String user, String text) { + AbstractChat chat = getChat(account, user); + if (chat == null) { + chat = createChat(account, user); + } + MessageItem messageItem = chat.newMessage(text); + chat.sendQueue(messageItem); + } + + /** + * @param account + * @param user + * @return Where there is active chat. + */ + public boolean hasActiveChat(String account, String user) { + AbstractChat chat = getChat(account, user); + return chat != null && chat.isActive(); + } + + /** + * @return Collection with active chats. + */ + public Collection getActiveChats() { + Collection collection = new ArrayList<>(); + for (AbstractChat chat : chats.values()) { + if (chat.isActive()) { + collection.add(chat); + } + } + return Collections.unmodifiableCollection(collection); + } + + /** + * Returns existed chat or create new one. + * + * @param account + * @param user + * @return + */ + public AbstractChat getOrCreateChat(String account, String user) { + AbstractChat chat = getChat(account, user); + if (chat == null) { + chat = createChat(account, user); + } + return chat; + } + + /** + * Force open chat (make it active). + * + * @param account + * @param user + */ + public void openChat(String account, String user) { + getOrCreateChat(account, user).openChat(); + } + + /** + * Closes specified chat (make it inactive). + * + * @param account + * @param user + */ + public void closeChat(String account, String user) { + AbstractChat chat = getChat(account, user); + if (chat == null) { + return; + } + chat.closeChat(); + } + + public void requestToLoadLocalHistory(String account, String user) { + AbstractChat chat = getChat(account, user); + if (chat == null) { + chat = createChat(account, user); + } + chat.requestToLoadLocalHistory(); + } + + /** + * @param account + * @param user + * @return Last incoming message's text. Empty string if last message is + * outgoing. + */ + public String getLastText(String account, String user) { + AbstractChat chat = getChat(account, user); + if (chat == null) { + return ""; + } + return chat.getLastText(); + } + + /** + * @param account + * @param user + * @return Time of last message in chat. Can be null. + */ + public Date getLastTime(String account, String user) { + AbstractChat chat = getChat(account, user); + if (chat == null) { + return null; + } + return chat.getLastTime(); + } + + /** + * Sets currently visible chat. + */ + public void setVisibleChat(BaseEntity visibleChat) { + final boolean remove = !AccountManager.getInstance().getArchiveMode(visibleChat.getAccount()).saveLocally(); + AbstractChat chat = getChat(visibleChat.getAccount(), visibleChat.getUser()); + if (chat == null) { + chat = createChat(visibleChat.getAccount(), visibleChat.getUser()); + } else { + // Mark messages as read and them delete from db if necessary. + final ArrayList messageItems = new ArrayList(); + for (MessageItem messageItem : chat.getMessages()) { + if (!messageItem.isRead()) { + messageItem.markAsRead(); + messageItems.add(messageItem); + } + } + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + Collection ids = getMessageIds(messageItems, remove); + if (remove) { + MessageTable.getInstance().removeMessages(ids); + } else { + MessageTable.getInstance().markAsRead(ids); + } + } + }); + } + this.visibleChat = chat; + } + + /** + * All chats become invisible. + */ + public void removeVisibleChat() { + visibleChat = null; + } + + /** + * @param chat + * @return Whether specified chat is currently visible. + */ + boolean isVisibleChat(AbstractChat chat) { + return visibleChat == chat; + } + + /** + * Removes all messages from chat. + * + * @param account + * @param user + */ + public void clearHistory(String account, String user) { + AbstractChat chat = getChat(account, user); + if (chat == null) { + return; + } + chat.removeAllMessages(); + onChatChanged(chat.getAccount(), chat.getUser(), false); + } + + /** + * Removes message from history. + * + * @param messageItem + */ + public void removeMessage(MessageItem messageItem) { + AbstractChat chat = messageItem.getChat(); + chat.removeMessage(messageItem); + onChatChanged(chat.getAccount(), chat.getUser(), false); + } + + /** + * @param account + * @param user + * @return List of messages or empty list. + */ + public Collection getMessages(String account, String user) { + AbstractChat chat = getChat(account, user); + if (chat == null) { + return Collections.emptyList(); + } + return chat.getMessages(); + } + + /** + * Called on action settings change. + */ + public void onSettingsChanged() { + ChatsShowStatusChange showStatusChange = SettingsManager.chatsShowStatusChange(); + Collection changedEntities = new ArrayList<>(); + for (AbstractChat chat : chats.values()) { + if ((chat instanceof RegularChat && showStatusChange != ChatsShowStatusChange.always) + || (chat instanceof RoomChat && showStatusChange == ChatsShowStatusChange.never)) { + // Remove actions with status change. + ArrayList remove = new ArrayList<>(); + for (MessageItem messageItem : chat.getMessages()) { + if (messageItem.getAction() != null && messageItem.getAction().isStatusChage()) { + remove.add(messageItem); + } + } + if (remove.isEmpty()) { + continue; + } + for (MessageItem messageItem : remove) { + chat.removeMessage(messageItem); + } + changedEntities.add(chat); + } + } + RosterManager.getInstance().onContactsChanged(changedEntities); + } + + @Override + public void onAccountArchiveModeChanged(AccountItem accountItem) { + final ArchiveMode archiveMode = AccountManager.getInstance().getArchiveMode(accountItem.getAccount()); + if (archiveMode.saveLocally()) { + return; + } + final String account = accountItem.getAccount(); + final ArrayList removeMessageItems = new ArrayList(); + for (AbstractChat chat : chats.getNested(account).values()) { + for (MessageItem messageItem : chat.getMessages()) { + if (archiveMode == ArchiveMode.dontStore || ((messageItem.isRead() + || archiveMode != ArchiveMode.unreadOnly) && messageItem.isSent())) { + removeMessageItems.add(messageItem); + } + } + } + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + // If message was read or received after removeMessageItems + // was created then it's ID will be not null. DB actions with + // such message will have no effect as if it was removed. + // History ids becomes invalid and will be cleared on next + // history load. + MessageTable.getInstance().removeMessages(getMessageIds(removeMessageItems, true)); + if (archiveMode == ArchiveMode.dontStore) { + MessageTable.getInstance().removeAccount(account); + } else if (archiveMode == ArchiveMode.unreadOnly) { + MessageTable.getInstance().removeReadAndSent(account); + } else { + MessageTable.getInstance().removeSent(account); + } + } + }); + AccountManager.getInstance().onAccountChanged(accountItem.getAccount()); + } + + @Override + public void onPacket(ConnectionItem connection, String bareAddress, Packet packet) { + if (!(connection instanceof AccountItem)) { + return; + } + String account = ((AccountItem) connection).getAccount(); + if (bareAddress == null) { + return; + } + if (packet instanceof Message + && MessageArchiveManager.getInstance().isModificationsSucceed(account) + && Delay.isOfflineMessage(Jid.getServer(account), packet)) { + // Ignore offline message if modification from server side message + // archive have been received. + return; + } + final String user = packet.getFrom(); + boolean processed = false; + for (AbstractChat chat : chats.getNested(account).values()) { + if (chat.onPacket(bareAddress, packet)) { + processed = true; + break; + } + } + if (getChat(account, user) != null) { + return; + } + if (!processed && packet instanceof Message) { + final Message message = (Message) packet; + final String body = message.getBody(); + if (body == null) { + return; + } + for (PacketExtension packetExtension : message.getExtensions()) { + if (packetExtension instanceof MUCUser) { + return; + } + } + createChat(account, user).onPacket(bareAddress, packet); + } + } + + public void displayForwardedMessage(ConnectionItem connection, Message message, Direction direction) { + + if (!(connection instanceof AccountItem)) + return; + String account = ((AccountItem) connection).getAccount(); + if (MessageArchiveManager.getInstance().isModificationsSucceed(account) + && Delay.isOfflineMessage(Jid.getServer(account), message)) { + // Ignore offline message if modification from server side message + // archive have been received. + return; + } + + if (direction == Direction.sent) { + String companion = Jid.getBareAddress(message.getTo()); + if (companion == null) { + return; + } + AbstractChat chat = getChat(account, companion); + if (chat == null) { + chat = createChat(account, companion); + } + String body = message.getBody(); + if (body == null) { + return; + } + chat.newMessage(body); + return; + } + + String companion = Jid.getBareAddress(message.getFrom()); + boolean processed = false; + for (AbstractChat chat : chats.getNested(account).values()) { + if (chat.onPacket(companion, message)) { + processed = true; + break; + } + } + if (getChat(account, companion) != null) { + return; + } + if (processed) { + return; + } + final String body = message.getBody(); + if (body == null) { + return; + } + createChat(account, companion).onPacket(companion, message); + + } + @Override + public void onRosterReceived(AccountItem accountItem) { + String account = accountItem.getAccount(); + for (AbstractChat chat : chats.getNested(account).values()) { + chat.onComplete(); + } + } + + @Override + public void onDisconnect(ConnectionItem connection) { + if (!(connection instanceof AccountItem)) { + return; + } + String account = ((AccountItem) connection).getAccount(); + for (AbstractChat chat : chats.getNested(account).values()) { + chat.onDisconnect(); + } + } + + @Override + public void onAccountRemoved(AccountItem accountItem) { + chats.clear(accountItem.getAccount()); + } + + @Override + public void onAccountDisabled(AccountItem accountItem) { + chats.clear(accountItem.getAccount()); + } + + /** + * Export chat to file with specified name. + * + * @param account + * @param user + * @param fileName + * @throws NetworkException + */ + public File exportChat(String account, String user, String fileName) throws NetworkException { + final File file = new File(Environment.getExternalStorageDirectory(), fileName); + try { + BufferedWriter out = new BufferedWriter(new FileWriter(file)); + final String titleName = RosterManager.getInstance().getName(account, user) + " (" + user + ")"; + out.write(""); + out.write(StringUtils.escapeHtml(titleName)); + out.write(""); + final AbstractChat abstractChat = getChat(account, user); + if (abstractChat != null) { + final boolean isMUC = abstractChat instanceof RoomChat; + final String accountName = AccountManager.getInstance().getNickName(account); + final String userName = RosterManager.getInstance().getName(account, user); + for (MessageItem messageItem : abstractChat.getMessages()) { + if (messageItem.getAction() != null) { + continue; + } + final String name; + if (isMUC) { + name = messageItem.getResource(); + } else { + if (messageItem.isIncoming()) { + name = userName; + } else { + name = accountName; + } + } + out.write(""); + out.write(StringUtils.escapeHtml(name)); + out.write(" ("); + out.write(StringUtils.getDateTimeText(messageItem.getTimestamp())); + out.write(")
\n

"); + out.write(StringUtils.escapeHtml(messageItem.getText())); + out.write("


\n"); + } + } + out.write(""); + out.close(); + } catch (IOException e) { + throw new NetworkException(R.string.FILE_NOT_FOUND); + } + return file; + } + + /** + * Notifies registered {@link OnChatChangedListener}. + * + * @param account + * @param user + * @param incoming + */ + public void onChatChanged(final String account, final String user, + final boolean incoming) { + Application.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + for (OnChatChangedListener onChatChangedListener + : Application.getInstance().getUIListeners(OnChatChangedListener.class)) { + onChatChangedListener.onChatChanged(account, user, incoming); + } + } + }); + } + + private boolean isStatusTrackingEnabled(String account, String bareAddress) { + if (SettingsManager.chatsShowStatusChange() != ChatsShowStatusChange.always) { + return false; + } + AbstractChat abstractChat = getChat(account, bareAddress); + return abstractChat != null && abstractChat instanceof RegularChat && abstractChat.isStatusTrackingEnabled(); + } + + @Override + public void onStatusChanged(String account, String bareAddress, String resource, String statusText) { + if (isStatusTrackingEnabled(account, bareAddress)) { + getChat(account, bareAddress).newAction(resource, statusText, ChatAction.status); + } + } + + @Override + public void onStatusChanged(String account, String bareAddress, String resource, + StatusMode statusMode, String statusText) { + if (isStatusTrackingEnabled(account, bareAddress)) { + getChat(account, bareAddress).newAction(resource, statusText, ChatAction.getChatAction(statusMode)); + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/data/message/MessageTable.java b/app/src/main/java/com/xabber/android/data/message/MessageTable.java index b0a3a4f633..56edeec686 100644 --- a/app/src/main/java/com/xabber/android/data/message/MessageTable.java +++ b/app/src/main/java/com/xabber/android/data/message/MessageTable.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -27,421 +27,421 @@ /** * Storage with messages. - * + * * @author alexander.ivanov */ class MessageTable extends AbstractEntityTable { - private static final class Fields implements AbstractEntityTable.Fields { - - private Fields() { - } - - /** - * Message archive collection tag. - */ - public static final String TAG = "tag"; - - /** - * User's resource or nick in chat room. - */ - public static final String RESOURCE = "resource"; - - /** - * Text message. - */ - public static final String TEXT = "text"; - - /** - * Message action. - *
    - *
  • Must be empty string for usual text message.
  • - *
  • Must be one of names in MessageAction.
  • - *
- * - * {@link #TEXT} can contains some description on this action. - */ - public static final String ACTION = "action"; - - /** - * Time when this message was created locally. - */ - public static final String TIMESTAMP = "timestamp"; - - /** - * Receive and send delay. - */ - public static final String DELAY_TIMESTAMP = "delay_timestamp"; - - /** - * Whether message is incoming. - */ - public static final String INCOMING = "incoming"; - - /** - * Whether incoming message was read. - */ - public static final String READ = "read"; - - /** - * Whether this outgoing message was sent. - */ - public static final String SENT = "sent"; - - /** - * Whether this outgoing message was not received. - */ - public static final String ERROR = "error"; - - } - - private static final String NAME = "messages"; - private static final String[] PROJECTION = new String[] { Fields._ID, - Fields.ACCOUNT, Fields.USER, Fields.RESOURCE, Fields.TEXT, - Fields.ACTION, Fields.TIMESTAMP, Fields.DELAY_TIMESTAMP, - Fields.INCOMING, Fields.READ, Fields.SENT, Fields.ERROR, Fields.TAG }; - - private final DatabaseManager databaseManager; - private SQLiteStatement insertNewMessageStatement; - private final Object insertNewMessageLock; - - private final static MessageTable instance; - - static { - instance = new MessageTable(DatabaseManager.getInstance()); - DatabaseManager.getInstance().addTable(instance); - } - - public static MessageTable getInstance() { - return instance; - } - - private MessageTable(DatabaseManager databaseManager) { - this.databaseManager = databaseManager; - insertNewMessageStatement = null; - insertNewMessageLock = new Object(); - } - - @Override - public void create(SQLiteDatabase db) { - String sql; - sql = "CREATE TABLE " + NAME + " (" + Fields._ID - + " INTEGER PRIMARY KEY," + Fields.ACCOUNT + " TEXT," - + Fields.USER + " TEXT," + Fields.RESOURCE + " TEXT," - + Fields.TEXT + " TEXT," + Fields.ACTION + " TEXT," - + Fields.TIMESTAMP + " INTEGER," + Fields.DELAY_TIMESTAMP - + " INTEGER," + Fields.INCOMING + " BOOLEAN," + Fields.READ - + " BOOLEAN," + Fields.SENT + " BOOLEAN," + Fields.ERROR - + " BOOLEAN," + Fields.TAG + " TEXT);"; - DatabaseManager.execSQL(db, sql); - sql = "CREATE INDEX " + NAME + "_list ON " + NAME + " (" - + Fields.ACCOUNT + ", " + Fields.USER + ", " + Fields.TIMESTAMP - + " ASC)"; - DatabaseManager.execSQL(db, sql); - } - - @Override - public void migrate(SQLiteDatabase db, int toVersion) { - super.migrate(db, toVersion); - String sql; - switch (toVersion) { - case 4: - sql = "CREATE TABLE messages (_id INTEGER PRIMARY KEY," - + "account INTEGER," + "user TEXT," + "text TEXT," - + "timestamp INTEGER," + "delay_timestamp INTEGER," - + "incoming BOOLEAN," + "read BOOLEAN," - + "notified BOOLEAN);"; - DatabaseManager.execSQL(db, sql); - sql = "CREATE INDEX messages_list ON messages (account, user, timestamp ASC);"; - DatabaseManager.execSQL(db, sql); - break; - case 8: - DatabaseManager.dropTable(db, "messages"); - sql = "CREATE TABLE messages (_id INTEGER PRIMARY KEY," - + "account TEXT," + "user TEXT," + "text TEXT," - + "timestamp INTEGER," + "delay_timestamp INTEGER," - + "incoming BOOLEAN," + "read BOOLEAN," - + "notified BOOLEAN);"; - DatabaseManager.execSQL(db, sql); - sql = "CREATE INDEX messages_list ON messages (account, user, timestamp ASC);"; - DatabaseManager.execSQL(db, sql); - break; - case 10: - sql = "ALTER TABLE messages ADD COLUMN send BOOLEAN;"; - DatabaseManager.execSQL(db, sql); - sql = "ALTER TABLE messages ADD COLUMN error BOOLEAN;"; - DatabaseManager.execSQL(db, sql); - sql = "UPDATE messages SET send = 1, error = 0 WHERE incoming = 0;"; - DatabaseManager.execSQL(db, sql); - break; - case 15: - sql = "UPDATE messages SET send = 1 WHERE incoming = 1;"; - DatabaseManager.execSQL(db, sql); - break; - case 17: - sql = "ALTER TABLE messages ADD COLUMN save BOOLEAN;"; - DatabaseManager.execSQL(db, sql); - sql = "UPDATE messages SET save = 1;"; - DatabaseManager.execSQL(db, sql); - break; - case 23: - sql = "ALTER TABLE messages ADD COLUMN resource TEXT;"; - DatabaseManager.execSQL(db, sql); - sql = "UPDATE messages SET resource = \"\";"; - DatabaseManager.execSQL(db, sql); - sql = "ALTER TABLE messages ADD COLUMN action TEXT;"; - DatabaseManager.execSQL(db, sql); - sql = "UPDATE messages SET action = \"\";"; - DatabaseManager.execSQL(db, sql); - break; - case 27: - DatabaseManager.renameTable(db, "messages", "old_messages"); - sql = "CREATE TABLE messages (_id INTEGER PRIMARY KEY," - + "account TEXT," + "user TEXT," + "resource TEXT," - + "text TEXT," + "action TEXT," + "timestamp INTEGER," - + "delay_timestamp INTEGER," + "incoming BOOLEAN," - + "read BOOLEAN," + "notified BOOLEAN," + "send BOOLEAN," - + "error BOOLEAN);"; - DatabaseManager.execSQL(db, sql); - sql = "INSERT INTO messages (" - + "account, user, resource, text, action, timestamp, delay_timestamp, incoming, read, notified, send, error" - + ") SELECT " - + "account, user, resource, text, action, timestamp, delay_timestamp, incoming, read, notified, send, error" - + " FROM old_messages WHERE save;"; - DatabaseManager.execSQL(db, sql); - DatabaseManager.dropTable(db, "old_messages"); - // Create index after drop old index. - sql = "CREATE INDEX messages_list ON messages (account, user, timestamp ASC);"; - DatabaseManager.execSQL(db, sql); - break; - case 28: - DatabaseManager.renameTable(db, "messages", "old_messages"); - sql = "CREATE TABLE messages (_id INTEGER PRIMARY KEY," - + "account TEXT," + "user TEXT," + "resource TEXT," - + "text TEXT," + "action TEXT," + "timestamp INTEGER," - + "delay_timestamp INTEGER," + "incoming BOOLEAN," - + "read BOOLEAN," + "notified BOOLEAN," + "sent BOOLEAN," - + "error BOOLEAN);"; - DatabaseManager.execSQL(db, sql); - sql = "INSERT INTO messages (" - + "account, user, resource, text, action, timestamp, delay_timestamp, incoming, read, notified, sent, error" - + ") SELECT " - + "account, user, resource, text, action, timestamp, delay_timestamp, incoming, read, notified, send, error" - + " FROM old_messages;"; - DatabaseManager.execSQL(db, sql); - DatabaseManager.dropTable(db, "old_messages"); - sql = "CREATE INDEX messages_list ON messages (account, user, timestamp ASC);"; - DatabaseManager.execSQL(db, sql); - break; - case 58: - sql = "ALTER TABLE messages ADD COLUMN tag TEXT;"; - DatabaseManager.execSQL(db, sql); - break; - case 61: - DatabaseManager.renameTable(db, "messages", "old_messages"); - sql = "CREATE TABLE messages (_id INTEGER PRIMARY KEY," - + "account TEXT," + "user TEXT," + "resource TEXT," - + "text TEXT," + "action TEXT," + "timestamp INTEGER," - + "delay_timestamp INTEGER," + "incoming BOOLEAN," - + "read BOOLEAN," + "sent BOOLEAN," + "error BOOLEAN," - + "tag TEXT);"; - DatabaseManager.execSQL(db, sql); - sql = "INSERT INTO messages (" - + "account, user, resource, text, action, timestamp, delay_timestamp, incoming, read, sent, error, tag" - + ") SELECT " - + "account, user, resource, text, action, timestamp, delay_timestamp, incoming, read, sent, error, tag" - + " FROM old_messages;"; - DatabaseManager.execSQL(db, sql); - DatabaseManager.dropTable(db, "old_messages"); - sql = "CREATE INDEX messages_list ON messages (account, user, timestamp ASC);"; - DatabaseManager.execSQL(db, sql); - break; - default: - break; - } - } - - /** - * Save new message to the database. - * - * @return Assigned id. - */ - long add(String account, String bareAddress, String tag, String resource, - String text, ChatAction action, Date timeStamp, - Date delayTimeStamp, boolean incoming, boolean read, boolean sent, - boolean error) { - final String actionString; - if (action == null) - actionString = ""; - else - actionString = action.name(); - synchronized (insertNewMessageLock) { - if (insertNewMessageStatement == null) { - SQLiteDatabase db = databaseManager.getWritableDatabase(); - insertNewMessageStatement = db.compileStatement("INSERT INTO " - + NAME + " (" + Fields.ACCOUNT + ", " + Fields.USER - + ", " + Fields.RESOURCE + ", " + Fields.TEXT + ", " - + Fields.ACTION + ", " + Fields.TIMESTAMP + ", " - + Fields.DELAY_TIMESTAMP + ", " + Fields.INCOMING - + ", " + Fields.READ + ", " + Fields.SENT + ", " - + Fields.ERROR + ", " + Fields.TAG + ") VALUES " - + "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"); - } - insertNewMessageStatement.bindString(1, account); - insertNewMessageStatement.bindString(2, bareAddress); - insertNewMessageStatement.bindString(3, resource); - insertNewMessageStatement.bindString(4, text); - insertNewMessageStatement.bindString(5, actionString); - insertNewMessageStatement.bindLong(6, timeStamp.getTime()); - if (delayTimeStamp == null) - insertNewMessageStatement.bindNull(7); - else - insertNewMessageStatement.bindLong(7, delayTimeStamp.getTime()); - insertNewMessageStatement.bindLong(8, incoming ? 1 : 0); - insertNewMessageStatement.bindLong(9, read ? 1 : 0); - insertNewMessageStatement.bindLong(10, sent ? 1 : 0); - insertNewMessageStatement.bindLong(11, error ? 1 : 0); - if (tag == null) - insertNewMessageStatement.bindNull(12); - else - insertNewMessageStatement.bindString(12, tag); - return insertNewMessageStatement.executeInsert(); - } - } - - void markAsRead(Collection ids) { - if (ids.isEmpty()) - return; - SQLiteDatabase db = databaseManager.getWritableDatabase(); - ContentValues values = new ContentValues(); - values.put(Fields.READ, 1); - db.update(NAME, values, DatabaseManager.in(Fields._ID, ids), null); - } - - void markAsSent(Collection ids) { - if (ids.isEmpty()) - return; - SQLiteDatabase db = databaseManager.getWritableDatabase(); - ContentValues values = new ContentValues(); - values.put(Fields.SENT, 1); - db.update(NAME, values, DatabaseManager.in(Fields._ID, ids), null); - } - - void markAsError(long id) { - SQLiteDatabase db = databaseManager.getWritableDatabase(); - ContentValues values = new ContentValues(); - values.put(Fields.ERROR, 1); - db.update(NAME, values, Fields._ID + " = ?", - new String[] { String.valueOf(id) }); - } - - /** - * @param account - * @param bareAddress - * @return Result set with messages for the chat. - */ - Cursor list(String account, String bareAddress) { - SQLiteDatabase db = databaseManager.getReadableDatabase(); - return db.query(NAME, PROJECTION, Fields.ACCOUNT + " = ? AND " - + Fields.USER + " = ?", new String[] { account, bareAddress }, - null, null, Fields.TIMESTAMP); - } - - /** - * @return Messages to be sent. - */ - Cursor messagesToSend() { - SQLiteDatabase db = databaseManager.getReadableDatabase(); - return db.query(NAME, PROJECTION, Fields.INCOMING + " = ? AND " - + Fields.SENT + " = ?", new String[] { "0", "0" }, null, null, - Fields.TIMESTAMP); - } - - /** - * Removes all read and sent messages. - * - * @param account - */ - void removeReadAndSent(String account) { - SQLiteDatabase db = databaseManager.getWritableDatabase(); - db.delete(NAME, Fields.ACCOUNT + " = ? AND " + Fields.READ - + " = ? AND " + Fields.SENT + " = ?", new String[] { account, - "1", "1" }); - } - - /** - * Removes all sent messages. - * - * @param account - */ - void removeSent(String account) { - SQLiteDatabase db = databaseManager.getWritableDatabase(); - db.delete(NAME, Fields.ACCOUNT + " = ? AND " + Fields.SENT + " = ?", - new String[] { account, "1", }); - } - - void removeMessages(Collection ids) { - if (ids.isEmpty()) - return; - SQLiteDatabase db = databaseManager.getWritableDatabase(); - db.delete(NAME, DatabaseManager.in(Fields._ID, ids), null); - } - - @Override - protected String getTableName() { - return NAME; - } - - @Override - protected String[] getProjection() { - return PROJECTION; - } - - static long getId(Cursor cursor) { - return cursor.getLong(cursor.getColumnIndex(Fields._ID)); - } - - static String getTag(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.TAG)); - } - - static String getResource(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.RESOURCE)); - } - - static String getText(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.TEXT)); - } - - static ChatAction getAction(Cursor cursor) { - return ChatAction.getChatAction(cursor.getString(cursor - .getColumnIndex(Fields.ACTION))); - } - - static boolean isIncoming(Cursor cursor) { - return cursor.getInt(cursor.getColumnIndex(Fields.INCOMING)) != 0; - } - - static boolean isSent(Cursor cursor) { - return cursor.getInt(cursor.getColumnIndex(Fields.SENT)) != 0; - } - - static boolean isRead(Cursor cursor) { - return cursor.getInt(cursor.getColumnIndex(Fields.READ)) != 0; - } - - static boolean hasError(Cursor cursor) { - return cursor.getInt(cursor.getColumnIndex(Fields.ERROR)) != 0; - } - - static Date getTimeStamp(Cursor cursor) { - return new Date(cursor.getLong(cursor.getColumnIndex(Fields.TIMESTAMP))); - } - - static Date getDelayTimeStamp(Cursor cursor) { - if (cursor.isNull(cursor.getColumnIndex(Fields.DELAY_TIMESTAMP))) - return null; - return new Date(cursor.getLong(cursor - .getColumnIndex(Fields.DELAY_TIMESTAMP))); - } + private static final class Fields implements AbstractEntityTable.Fields { + + private Fields() { + } + + /** + * Message archive collection tag. + */ + public static final String TAG = "tag"; + + /** + * User's resource or nick in chat room. + */ + public static final String RESOURCE = "resource"; + + /** + * Text message. + */ + public static final String TEXT = "text"; + + /** + * Message action. + *
    + *
  • Must be empty string for usual text message.
  • + *
  • Must be one of names in MessageAction.
  • + *
+ *

+ * {@link #TEXT} can contains some description on this action. + */ + public static final String ACTION = "action"; + + /** + * Time when this message was created locally. + */ + public static final String TIMESTAMP = "timestamp"; + + /** + * Receive and send delay. + */ + public static final String DELAY_TIMESTAMP = "delay_timestamp"; + + /** + * Whether message is incoming. + */ + public static final String INCOMING = "incoming"; + + /** + * Whether incoming message was read. + */ + public static final String READ = "read"; + + /** + * Whether this outgoing message was sent. + */ + public static final String SENT = "sent"; + + /** + * Whether this outgoing message was not received. + */ + public static final String ERROR = "error"; + + } + + private static final String NAME = "messages"; + private static final String[] PROJECTION = new String[]{Fields._ID, + Fields.ACCOUNT, Fields.USER, Fields.RESOURCE, Fields.TEXT, + Fields.ACTION, Fields.TIMESTAMP, Fields.DELAY_TIMESTAMP, + Fields.INCOMING, Fields.READ, Fields.SENT, Fields.ERROR, Fields.TAG}; + + private final DatabaseManager databaseManager; + private SQLiteStatement insertNewMessageStatement; + private final Object insertNewMessageLock; + + private final static MessageTable instance; + + static { + instance = new MessageTable(DatabaseManager.getInstance()); + DatabaseManager.getInstance().addTable(instance); + } + + public static MessageTable getInstance() { + return instance; + } + + private MessageTable(DatabaseManager databaseManager) { + this.databaseManager = databaseManager; + insertNewMessageStatement = null; + insertNewMessageLock = new Object(); + } + + @Override + public void create(SQLiteDatabase db) { + String sql; + sql = "CREATE TABLE " + NAME + " (" + Fields._ID + + " INTEGER PRIMARY KEY," + Fields.ACCOUNT + " TEXT," + + Fields.USER + " TEXT," + Fields.RESOURCE + " TEXT," + + Fields.TEXT + " TEXT," + Fields.ACTION + " TEXT," + + Fields.TIMESTAMP + " INTEGER," + Fields.DELAY_TIMESTAMP + + " INTEGER," + Fields.INCOMING + " BOOLEAN," + Fields.READ + + " BOOLEAN," + Fields.SENT + " BOOLEAN," + Fields.ERROR + + " BOOLEAN," + Fields.TAG + " TEXT);"; + DatabaseManager.execSQL(db, sql); + sql = "CREATE INDEX " + NAME + "_list ON " + NAME + " (" + + Fields.ACCOUNT + ", " + Fields.USER + ", " + Fields.TIMESTAMP + + " ASC)"; + DatabaseManager.execSQL(db, sql); + } + + @Override + public void migrate(SQLiteDatabase db, int toVersion) { + super.migrate(db, toVersion); + String sql; + switch (toVersion) { + case 4: + sql = "CREATE TABLE messages (_id INTEGER PRIMARY KEY," + + "account INTEGER," + "user TEXT," + "text TEXT," + + "timestamp INTEGER," + "delay_timestamp INTEGER," + + "incoming BOOLEAN," + "read BOOLEAN," + + "notified BOOLEAN);"; + DatabaseManager.execSQL(db, sql); + sql = "CREATE INDEX messages_list ON messages (account, user, timestamp ASC);"; + DatabaseManager.execSQL(db, sql); + break; + case 8: + DatabaseManager.dropTable(db, "messages"); + sql = "CREATE TABLE messages (_id INTEGER PRIMARY KEY," + + "account TEXT," + "user TEXT," + "text TEXT," + + "timestamp INTEGER," + "delay_timestamp INTEGER," + + "incoming BOOLEAN," + "read BOOLEAN," + + "notified BOOLEAN);"; + DatabaseManager.execSQL(db, sql); + sql = "CREATE INDEX messages_list ON messages (account, user, timestamp ASC);"; + DatabaseManager.execSQL(db, sql); + break; + case 10: + sql = "ALTER TABLE messages ADD COLUMN send BOOLEAN;"; + DatabaseManager.execSQL(db, sql); + sql = "ALTER TABLE messages ADD COLUMN error BOOLEAN;"; + DatabaseManager.execSQL(db, sql); + sql = "UPDATE messages SET send = 1, error = 0 WHERE incoming = 0;"; + DatabaseManager.execSQL(db, sql); + break; + case 15: + sql = "UPDATE messages SET send = 1 WHERE incoming = 1;"; + DatabaseManager.execSQL(db, sql); + break; + case 17: + sql = "ALTER TABLE messages ADD COLUMN save BOOLEAN;"; + DatabaseManager.execSQL(db, sql); + sql = "UPDATE messages SET save = 1;"; + DatabaseManager.execSQL(db, sql); + break; + case 23: + sql = "ALTER TABLE messages ADD COLUMN resource TEXT;"; + DatabaseManager.execSQL(db, sql); + sql = "UPDATE messages SET resource = \"\";"; + DatabaseManager.execSQL(db, sql); + sql = "ALTER TABLE messages ADD COLUMN action TEXT;"; + DatabaseManager.execSQL(db, sql); + sql = "UPDATE messages SET action = \"\";"; + DatabaseManager.execSQL(db, sql); + break; + case 27: + DatabaseManager.renameTable(db, "messages", "old_messages"); + sql = "CREATE TABLE messages (_id INTEGER PRIMARY KEY," + + "account TEXT," + "user TEXT," + "resource TEXT," + + "text TEXT," + "action TEXT," + "timestamp INTEGER," + + "delay_timestamp INTEGER," + "incoming BOOLEAN," + + "read BOOLEAN," + "notified BOOLEAN," + "send BOOLEAN," + + "error BOOLEAN);"; + DatabaseManager.execSQL(db, sql); + sql = "INSERT INTO messages (" + + "account, user, resource, text, action, timestamp, delay_timestamp, incoming, read, notified, send, error" + + ") SELECT " + + "account, user, resource, text, action, timestamp, delay_timestamp, incoming, read, notified, send, error" + + " FROM old_messages WHERE save;"; + DatabaseManager.execSQL(db, sql); + DatabaseManager.dropTable(db, "old_messages"); + // Create index after drop old index. + sql = "CREATE INDEX messages_list ON messages (account, user, timestamp ASC);"; + DatabaseManager.execSQL(db, sql); + break; + case 28: + DatabaseManager.renameTable(db, "messages", "old_messages"); + sql = "CREATE TABLE messages (_id INTEGER PRIMARY KEY," + + "account TEXT," + "user TEXT," + "resource TEXT," + + "text TEXT," + "action TEXT," + "timestamp INTEGER," + + "delay_timestamp INTEGER," + "incoming BOOLEAN," + + "read BOOLEAN," + "notified BOOLEAN," + "sent BOOLEAN," + + "error BOOLEAN);"; + DatabaseManager.execSQL(db, sql); + sql = "INSERT INTO messages (" + + "account, user, resource, text, action, timestamp, delay_timestamp, incoming, read, notified, sent, error" + + ") SELECT " + + "account, user, resource, text, action, timestamp, delay_timestamp, incoming, read, notified, send, error" + + " FROM old_messages;"; + DatabaseManager.execSQL(db, sql); + DatabaseManager.dropTable(db, "old_messages"); + sql = "CREATE INDEX messages_list ON messages (account, user, timestamp ASC);"; + DatabaseManager.execSQL(db, sql); + break; + case 58: + sql = "ALTER TABLE messages ADD COLUMN tag TEXT;"; + DatabaseManager.execSQL(db, sql); + break; + case 61: + DatabaseManager.renameTable(db, "messages", "old_messages"); + sql = "CREATE TABLE messages (_id INTEGER PRIMARY KEY," + + "account TEXT," + "user TEXT," + "resource TEXT," + + "text TEXT," + "action TEXT," + "timestamp INTEGER," + + "delay_timestamp INTEGER," + "incoming BOOLEAN," + + "read BOOLEAN," + "sent BOOLEAN," + "error BOOLEAN," + + "tag TEXT);"; + DatabaseManager.execSQL(db, sql); + sql = "INSERT INTO messages (" + + "account, user, resource, text, action, timestamp, delay_timestamp, incoming, read, sent, error, tag" + + ") SELECT " + + "account, user, resource, text, action, timestamp, delay_timestamp, incoming, read, sent, error, tag" + + " FROM old_messages;"; + DatabaseManager.execSQL(db, sql); + DatabaseManager.dropTable(db, "old_messages"); + sql = "CREATE INDEX messages_list ON messages (account, user, timestamp ASC);"; + DatabaseManager.execSQL(db, sql); + break; + default: + break; + } + } + + /** + * Save new message to the database. + * + * @return Assigned id. + */ + long add(String account, String bareAddress, String tag, String resource, + String text, ChatAction action, Date timeStamp, + Date delayTimeStamp, boolean incoming, boolean read, boolean sent, + boolean error) { + final String actionString; + if (action == null) + actionString = ""; + else + actionString = action.name(); + synchronized (insertNewMessageLock) { + if (insertNewMessageStatement == null) { + SQLiteDatabase db = databaseManager.getWritableDatabase(); + insertNewMessageStatement = db.compileStatement("INSERT INTO " + + NAME + " (" + Fields.ACCOUNT + ", " + Fields.USER + + ", " + Fields.RESOURCE + ", " + Fields.TEXT + ", " + + Fields.ACTION + ", " + Fields.TIMESTAMP + ", " + + Fields.DELAY_TIMESTAMP + ", " + Fields.INCOMING + + ", " + Fields.READ + ", " + Fields.SENT + ", " + + Fields.ERROR + ", " + Fields.TAG + ") VALUES " + + "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"); + } + insertNewMessageStatement.bindString(1, account); + insertNewMessageStatement.bindString(2, bareAddress); + insertNewMessageStatement.bindString(3, resource); + insertNewMessageStatement.bindString(4, text); + insertNewMessageStatement.bindString(5, actionString); + insertNewMessageStatement.bindLong(6, timeStamp.getTime()); + if (delayTimeStamp == null) + insertNewMessageStatement.bindNull(7); + else + insertNewMessageStatement.bindLong(7, delayTimeStamp.getTime()); + insertNewMessageStatement.bindLong(8, incoming ? 1 : 0); + insertNewMessageStatement.bindLong(9, read ? 1 : 0); + insertNewMessageStatement.bindLong(10, sent ? 1 : 0); + insertNewMessageStatement.bindLong(11, error ? 1 : 0); + if (tag == null) + insertNewMessageStatement.bindNull(12); + else + insertNewMessageStatement.bindString(12, tag); + return insertNewMessageStatement.executeInsert(); + } + } + + void markAsRead(Collection ids) { + if (ids.isEmpty()) + return; + SQLiteDatabase db = databaseManager.getWritableDatabase(); + ContentValues values = new ContentValues(); + values.put(Fields.READ, 1); + db.update(NAME, values, DatabaseManager.in(Fields._ID, ids), null); + } + + void markAsSent(Collection ids) { + if (ids.isEmpty()) + return; + SQLiteDatabase db = databaseManager.getWritableDatabase(); + ContentValues values = new ContentValues(); + values.put(Fields.SENT, 1); + db.update(NAME, values, DatabaseManager.in(Fields._ID, ids), null); + } + + void markAsError(long id) { + SQLiteDatabase db = databaseManager.getWritableDatabase(); + ContentValues values = new ContentValues(); + values.put(Fields.ERROR, 1); + db.update(NAME, values, Fields._ID + " = ?", + new String[]{String.valueOf(id)}); + } + + /** + * @param account + * @param bareAddress + * @return Result set with messages for the chat. + */ + Cursor list(String account, String bareAddress) { + SQLiteDatabase db = databaseManager.getReadableDatabase(); + return db.query(NAME, PROJECTION, Fields.ACCOUNT + " = ? AND " + + Fields.USER + " = ?", new String[]{account, bareAddress}, + null, null, Fields.TIMESTAMP); + } + + /** + * @return Messages to be sent. + */ + Cursor messagesToSend() { + SQLiteDatabase db = databaseManager.getReadableDatabase(); + return db.query(NAME, PROJECTION, Fields.INCOMING + " = ? AND " + + Fields.SENT + " = ?", new String[]{"0", "0"}, null, null, + Fields.TIMESTAMP); + } + + /** + * Removes all read and sent messages. + * + * @param account + */ + void removeReadAndSent(String account) { + SQLiteDatabase db = databaseManager.getWritableDatabase(); + db.delete(NAME, Fields.ACCOUNT + " = ? AND " + Fields.READ + + " = ? AND " + Fields.SENT + " = ?", new String[]{account, + "1", "1"}); + } + + /** + * Removes all sent messages. + * + * @param account + */ + void removeSent(String account) { + SQLiteDatabase db = databaseManager.getWritableDatabase(); + db.delete(NAME, Fields.ACCOUNT + " = ? AND " + Fields.SENT + " = ?", + new String[]{account, "1",}); + } + + void removeMessages(Collection ids) { + if (ids.isEmpty()) + return; + SQLiteDatabase db = databaseManager.getWritableDatabase(); + db.delete(NAME, DatabaseManager.in(Fields._ID, ids), null); + } + + @Override + protected String getTableName() { + return NAME; + } + + @Override + protected String[] getProjection() { + return PROJECTION; + } + + static long getId(Cursor cursor) { + return cursor.getLong(cursor.getColumnIndex(Fields._ID)); + } + + static String getTag(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.TAG)); + } + + static String getResource(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.RESOURCE)); + } + + static String getText(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.TEXT)); + } + + static ChatAction getAction(Cursor cursor) { + return ChatAction.getChatAction(cursor.getString(cursor + .getColumnIndex(Fields.ACTION))); + } + + static boolean isIncoming(Cursor cursor) { + return cursor.getInt(cursor.getColumnIndex(Fields.INCOMING)) != 0; + } + + static boolean isSent(Cursor cursor) { + return cursor.getInt(cursor.getColumnIndex(Fields.SENT)) != 0; + } + + static boolean isRead(Cursor cursor) { + return cursor.getInt(cursor.getColumnIndex(Fields.READ)) != 0; + } + + static boolean hasError(Cursor cursor) { + return cursor.getInt(cursor.getColumnIndex(Fields.ERROR)) != 0; + } + + static Date getTimeStamp(Cursor cursor) { + return new Date(cursor.getLong(cursor.getColumnIndex(Fields.TIMESTAMP))); + } + + static Date getDelayTimeStamp(Cursor cursor) { + if (cursor.isNull(cursor.getColumnIndex(Fields.DELAY_TIMESTAMP))) + return null; + return new Date(cursor.getLong(cursor + .getColumnIndex(Fields.DELAY_TIMESTAMP))); + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/data/message/OnChatChangedListener.java b/app/src/main/java/com/xabber/android/data/message/OnChatChangedListener.java index 4f104e66b5..4530e23d84 100644 --- a/app/src/main/java/com/xabber/android/data/message/OnChatChangedListener.java +++ b/app/src/main/java/com/xabber/android/data/message/OnChatChangedListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,18 +18,16 @@ /** * Listener for changes in the chat. - * + * * @author alexander.ivanov - * */ public interface OnChatChangedListener extends BaseUIListener { - /** - * @param account - * @param user - * @param incoming - * Whether new incoming message received. - */ - public void onChatChanged(String account, String user, boolean incoming); + /** + * @param account + * @param user + * @param incoming Whether new incoming message received. + */ + void onChatChanged(String account, String user, boolean incoming); } diff --git a/app/src/main/java/com/xabber/android/data/message/ReceiptManager.java b/app/src/main/java/com/xabber/android/data/message/ReceiptManager.java index f0afd13262..1780376a61 100644 --- a/app/src/main/java/com/xabber/android/data/message/ReceiptManager.java +++ b/app/src/main/java/com/xabber/android/data/message/ReceiptManager.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -36,124 +36,123 @@ /** * Manage message receive receipts as well as error replies. - * + * * @author alexander.ivanov - * */ public class ReceiptManager implements OnPacketListener, OnDisconnectListener { - /** - * Sent messages for packet ids in accounts. - */ - private final NestedMap sent; + /** + * Sent messages for packet ids in accounts. + */ + private final NestedMap sent; - private final static ReceiptManager instance; + private final static ReceiptManager instance; - static { - instance = new ReceiptManager(); - Application.getInstance().addManager(instance); + static { + instance = new ReceiptManager(); + Application.getInstance().addManager(instance); - Connection - .addConnectionCreationListener(new ConnectionCreationListener() { - @Override - public void connectionCreated(final Connection connection) { - ServiceDiscoveryManager.getInstanceFor(connection) - .addFeature("urn:xmpp:receipts"); - } - }); - } + Connection + .addConnectionCreationListener(new ConnectionCreationListener() { + @Override + public void connectionCreated(final Connection connection) { + ServiceDiscoveryManager.getInstanceFor(connection) + .addFeature("urn:xmpp:receipts"); + } + }); + } - public static ReceiptManager getInstance() { - return instance; - } + public static ReceiptManager getInstance() { + return instance; + } - private ReceiptManager() { - sent = new NestedMap(); - } + private ReceiptManager() { + sent = new NestedMap(); + } - /** - * Update outgoing message before sending. - * - * @param abstractChat - * @param message - * @param messageItem - */ - public void updateOutgoingMessage(AbstractChat abstractChat, - Message message, MessageItem messageItem) { - sent.put(abstractChat.getAccount(), message.getPacketID(), messageItem); - if (abstractChat instanceof RoomChat) - return; - message.addExtension(new Request()); - } + /** + * Update outgoing message before sending. + * + * @param abstractChat + * @param message + * @param messageItem + */ + public void updateOutgoingMessage(AbstractChat abstractChat, + Message message, MessageItem messageItem) { + sent.put(abstractChat.getAccount(), message.getPacketID(), messageItem); + if (abstractChat instanceof RoomChat) + return; + message.addExtension(new Request()); + } - @Override - public void onPacket(ConnectionItem connection, String bareAddress, - Packet packet) { - if (!(connection instanceof AccountItem)) - return; - String account = ((AccountItem) connection).getAccount(); - final String user = packet.getFrom(); - if (user == null) - return; - if (!(packet instanceof Message)) - return; - final Message message = (Message) packet; - if (message.getType() == Message.Type.error) { - final MessageItem messageItem = sent.remove(account, - message.getPacketID()); - if (messageItem != null && !messageItem.isError()) { - messageItem.markAsError(); - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - if (messageItem.getId() != null) - MessageTable.getInstance().markAsError( - messageItem.getId()); - } - }); - MessageManager.getInstance().onChatChanged( - messageItem.getChat().getAccount(), - messageItem.getChat().getUser(), false); - } - } else { - for (PacketExtension packetExtension : message.getExtensions()) - if (packetExtension instanceof Received) { - Received received = (Received) packetExtension; - String id = received.getId(); - if (id == null) - id = message.getPacketID(); - if (id == null) - continue; - final MessageItem messageItem = sent.remove(account, id); - if (messageItem != null && !messageItem.isDelivered()) { - messageItem.markAsDelivered(); - MessageManager.getInstance().onChatChanged( - messageItem.getChat().getAccount(), - messageItem.getChat().getUser(), false); - } - } else if (packetExtension instanceof Request) { - String id = message.getPacketID(); - if (id == null) - continue; - Message receipt = new Message(user); - receipt.addExtension(new Received(id)); - receipt.setThread(message.getThread()); - try { - ConnectionManager.getInstance().sendPacket(account, - receipt); - } catch (NetworkException e) { - LogManager.exception(this, e); - } - } - } - } + @Override + public void onPacket(ConnectionItem connection, String bareAddress, + Packet packet) { + if (!(connection instanceof AccountItem)) + return; + String account = ((AccountItem) connection).getAccount(); + final String user = packet.getFrom(); + if (user == null) + return; + if (!(packet instanceof Message)) + return; + final Message message = (Message) packet; + if (message.getType() == Message.Type.error) { + final MessageItem messageItem = sent.remove(account, + message.getPacketID()); + if (messageItem != null && !messageItem.isError()) { + messageItem.markAsError(); + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + if (messageItem.getId() != null) + MessageTable.getInstance().markAsError( + messageItem.getId()); + } + }); + MessageManager.getInstance().onChatChanged( + messageItem.getChat().getAccount(), + messageItem.getChat().getUser(), false); + } + } else { + for (PacketExtension packetExtension : message.getExtensions()) + if (packetExtension instanceof Received) { + Received received = (Received) packetExtension; + String id = received.getId(); + if (id == null) + id = message.getPacketID(); + if (id == null) + continue; + final MessageItem messageItem = sent.remove(account, id); + if (messageItem != null && !messageItem.isDelivered()) { + messageItem.markAsDelivered(); + MessageManager.getInstance().onChatChanged( + messageItem.getChat().getAccount(), + messageItem.getChat().getUser(), false); + } + } else if (packetExtension instanceof Request) { + String id = message.getPacketID(); + if (id == null) + continue; + Message receipt = new Message(user); + receipt.addExtension(new Received(id)); + receipt.setThread(message.getThread()); + try { + ConnectionManager.getInstance().sendPacket(account, + receipt); + } catch (NetworkException e) { + LogManager.exception(this, e); + } + } + } + } - @Override - public void onDisconnect(ConnectionItem connection) { - if (!(connection instanceof AccountItem)) - return; - String account = ((AccountItem) connection).getAccount(); - sent.clear(account); - } + @Override + public void onDisconnect(ConnectionItem connection) { + if (!(connection instanceof AccountItem)) + return; + String account = ((AccountItem) connection).getAccount(); + sent.clear(account); + } } diff --git a/app/src/main/java/com/xabber/android/data/message/RegularChat.java b/app/src/main/java/com/xabber/android/data/message/RegularChat.java index 23ce9c2b8f..54615cd2ec 100644 --- a/app/src/main/java/com/xabber/android/data/message/RegularChat.java +++ b/app/src/main/java/com/xabber/android/data/message/RegularChat.java @@ -1,27 +1,19 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data.message; -import net.java.otr4j.OtrException; - -import org.jivesoftware.smack.packet.Message; -import org.jivesoftware.smack.packet.Message.Type; -import org.jivesoftware.smack.packet.Packet; -import org.jivesoftware.smack.packet.Presence; -import org.jivesoftware.smackx.packet.MUCUser; - import com.xabber.android.data.LogManager; import com.xabber.android.data.NetworkException; import com.xabber.android.data.SettingsManager; @@ -35,149 +27,160 @@ import com.xabber.xmpp.delay.Delay; import com.xabber.xmpp.muc.MUC; +import net.java.otr4j.OtrException; + +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.packet.Message.Type; +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smack.packet.Presence; +import org.jivesoftware.smackx.packet.MUCUser; + /** * Represents normal chat. - * + * * @author alexander.ivanov - * */ public class RegularChat extends AbstractChat { - /** - * Resource used for contact. - */ - private String resource; - - RegularChat(String account, String user) { - super(account, user); - resource = null; - } - - public String getResource() { - return resource; - } - - @Override - public String getTo() { - if (resource == null) - return user; - else - return user + "/" + resource; - } - - @Override - public Type getType() { - return Type.chat; - } - - @Override - protected boolean canSendMessage() { - if (super.canSendMessage()) { - if (SettingsManager.securityOtrMode() != SecurityOtrMode.required) - return true; - SecurityLevel securityLevel = OTRManager.getInstance() - .getSecurityLevel(account, user); - if (securityLevel != SecurityLevel.plain) - return true; - try { - OTRManager.getInstance().startSession(account, user); - } catch (NetworkException e) { - } - } - return false; - } - - @Override - protected String prepareText(String text) { - text = super.prepareText(text); - try { - return OTRManager.getInstance().transformSending(account, user, - text); - } catch (OtrException e) { - LogManager.exception(this, e); - return null; - } - } - - @Override - protected MessageItem newMessage(String text) { - return newMessage( - null, - text, - null, - null, - false, - false, - false, - false, - MessageArchiveManager.getInstance().getSaveMode(account, user, - getThreadId()) != SaveMode.fls); - } - - @Override - protected boolean onPacket(String bareAddress, Packet packet) { - if (!super.onPacket(bareAddress, packet)) - return false; - final String resource = Jid.getResource(packet.getFrom()); - if (packet instanceof Presence) { - final Presence presence = (Presence) packet; - if (this.resource != null - && presence.getType() == Presence.Type.unavailable - && this.resource.equals(resource)) - this.resource = null; - } else if (packet instanceof Message) { - final Message message = (Message) packet; - if (message.getType() == Message.Type.error) - return true; - - MUCUser mucUser = MUC.getMUCUserExtension(message); - if (mucUser != null && mucUser.getInvite() != null) - return true; - - String text = message.getBody(); - if (text == null) - return true; - - String thread = message.getThread(); - updateThreadId(thread); - boolean unencrypted = false; - try { - text = OTRManager.getInstance().transformReceiving(account, - user, text); - } catch (OtrException e) { - if (e.getCause() instanceof OTRUnencryptedException) { - text = ((OTRUnencryptedException) e.getCause()).getText(); - unencrypted = true; - } else { - LogManager.exception(this, e); - // Invalid message received. - return true; - } - } - // System message received. - if (text == null) - return true; - if (!"".equals(resource)) - this.resource = resource; - newMessage( - resource, - text, - null, - Delay.getDelay(message), - true, - true, - unencrypted, - Delay.isOfflineMessage(Jid.getServer(account), packet), - MessageArchiveManager.getInstance().getSaveMode(account, - user, getThreadId()) != SaveMode.fls); - } - return true; - } - - @Override - protected void onComplete() { - super.onComplete(); - sendMessages(); - } + /** + * Resource used for contact. + */ + private String resource; + + RegularChat(String account, String user) { + super(account, user); + resource = null; + } + + public String getResource() { + return resource; + } + + @Override + public String getTo() { + if (resource == null) + return user; + else + return user + "/" + resource; + } + + @Override + public Type getType() { + return Type.chat; + } + + @Override + protected boolean canSendMessage() { + if (super.canSendMessage()) { + if (SettingsManager.securityOtrMode() != SecurityOtrMode.required) + return true; + SecurityLevel securityLevel = OTRManager.getInstance() + .getSecurityLevel(account, user); + if (securityLevel != SecurityLevel.plain) + return true; + try { + OTRManager.getInstance().startSession(account, user); + } catch (NetworkException e) { + } + } + return false; + } + + @Override + protected String prepareText(String text) { + text = super.prepareText(text); + try { + return OTRManager.getInstance().transformSending(account, user, + text); + } catch (OtrException e) { + LogManager.exception(this, e); + return null; + } + } + + @Override + protected MessageItem newMessage(String text) { + return newMessage( + null, + text, + null, + null, + false, + false, + false, + false, + MessageArchiveManager.getInstance().getSaveMode(account, user, + getThreadId()) != SaveMode.fls); + } + + @Override + protected boolean onPacket(String bareAddress, Packet packet) { + if (!super.onPacket(bareAddress, packet)) + return false; + final String resource = Jid.getResource(packet.getFrom()); + if (packet instanceof Presence) { + final Presence presence = (Presence) packet; + + if (this.resource != null && presence.getType() == Presence.Type.unavailable + && this.resource.equals(resource)) { + this.resource = null; + } + + if (presence.getType() == Presence.Type.unavailable) { + OTRManager.getInstance().onContactUnAvailable(account, user); + } + } else if (packet instanceof Message) { + final Message message = (Message) packet; + if (message.getType() == Message.Type.error) + return true; + + MUCUser mucUser = MUC.getMUCUserExtension(message); + if (mucUser != null && mucUser.getInvite() != null) + return true; + + String text = message.getBody(); + if (text == null) + return true; + + String thread = message.getThread(); + updateThreadId(thread); + boolean unencrypted = false; + try { + text = OTRManager.getInstance().transformReceiving(account, user, text); + } catch (OtrException e) { + if (e.getCause() instanceof OTRUnencryptedException) { + text = ((OTRUnencryptedException) e.getCause()).getText(); + unencrypted = true; + } else { + LogManager.exception(this, e); + // Invalid message received. + return true; + } + } + // System message received. + if (text == null || text.trim().equals("")) + return true; + if (!"".equals(resource)) + this.resource = resource; + newMessage( + resource, + text, + null, + Delay.getDelay(message), + true, + true, + unencrypted, + Delay.isOfflineMessage(Jid.getServer(account), packet), + MessageArchiveManager.getInstance().getSaveMode(account, + user, getThreadId()) != SaveMode.fls); + } + return true; + } + + @Override + protected void onComplete() { + super.onComplete(); + sendMessages(); + } } diff --git a/app/src/main/java/com/xabber/android/data/message/chat/AbstractChatPropertyTable.java b/app/src/main/java/com/xabber/android/data/message/chat/AbstractChatPropertyTable.java index 7ceb05821e..3e5d9703e2 100644 --- a/app/src/main/java/com/xabber/android/data/message/chat/AbstractChatPropertyTable.java +++ b/app/src/main/java/com/xabber/android/data/message/chat/AbstractChatPropertyTable.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -22,96 +22,95 @@ /** * Table with custom values associated with chat. - * + * * @author alexander.ivanov - * */ abstract class AbstractChatPropertyTable extends AbstractEntityTable { - static final class Fields implements AbstractEntityTable.Fields { - - private Fields() { - } - - public static final String VALUE = "value"; - - } - - private static final String[] PROJECTION = new String[] { Fields.ACCOUNT, - Fields.USER, Fields.VALUE }; - - private final DatabaseManager databaseManager; - private SQLiteStatement writeStatement; - private final Object writeLock; - - AbstractChatPropertyTable(DatabaseManager databaseManager) { - this.databaseManager = databaseManager; - writeStatement = null; - writeLock = new Object(); - } - - /** - * @return SQL type of the value field. - */ - abstract String getValueType(); - - /** - * Hook to bind value as 3 argument to the statement. - * - * @param writeStatement - * @param value - */ - abstract void bindValue(SQLiteStatement writeStatement, T value); - - @Override - protected String[] getProjection() { - return PROJECTION; - } - - @Override - public void create(SQLiteDatabase db) { - String sql = "CREATE TABLE " + getTableName() + " (" + Fields.ACCOUNT - + " TEXT," + Fields.USER + " TEXT," + Fields.VALUE + " " - + getValueType() + ");"; - DatabaseManager.execSQL(db, sql); - sql = "CREATE UNIQUE INDEX " + getTableName() + "_index ON " - + getTableName() + " " + "(" + Fields.ACCOUNT + ", " - + Fields.USER + ");"; - DatabaseManager.execSQL(db, sql); - } - - /** - * Initial migrate to create table. - * - * @param db - * @param toVersion - * @param tableName - * @param valueType - */ - void initialMigrate(SQLiteDatabase db, String tableName, String valueType) { - String sql; - sql = "CREATE TABLE " + tableName + " (" + "account TEXT," - + "user TEXT," + "value " + valueType + ");"; - DatabaseManager.execSQL(db, sql); - sql = "CREATE UNIQUE INDEX " + tableName + "_index ON " + tableName - + " (account, user);"; - DatabaseManager.execSQL(db, sql); - } - - void write(String account, String user, T value) { - synchronized (writeLock) { - if (writeStatement == null) { - SQLiteDatabase db = databaseManager.getWritableDatabase(); - writeStatement = db.compileStatement("INSERT OR REPLACE INTO " - + getTableName() + " (" + Fields.ACCOUNT + ", " - + Fields.USER + ", " + Fields.VALUE - + ") VALUES (?, ?, ?);"); - } - writeStatement.bindString(1, account); - writeStatement.bindString(2, user); - bindValue(writeStatement, value); - writeStatement.execute(); - } - } + static final class Fields implements AbstractEntityTable.Fields { + + private Fields() { + } + + public static final String VALUE = "value"; + + } + + private static final String[] PROJECTION = new String[]{Fields.ACCOUNT, + Fields.USER, Fields.VALUE}; + + private final DatabaseManager databaseManager; + private SQLiteStatement writeStatement; + private final Object writeLock; + + AbstractChatPropertyTable(DatabaseManager databaseManager) { + this.databaseManager = databaseManager; + writeStatement = null; + writeLock = new Object(); + } + + /** + * @return SQL type of the value field. + */ + abstract String getValueType(); + + /** + * Hook to bind value as 3 argument to the statement. + * + * @param writeStatement + * @param value + */ + abstract void bindValue(SQLiteStatement writeStatement, T value); + + @Override + protected String[] getProjection() { + return PROJECTION; + } + + @Override + public void create(SQLiteDatabase db) { + String sql = "CREATE TABLE " + getTableName() + " (" + Fields.ACCOUNT + + " TEXT," + Fields.USER + " TEXT," + Fields.VALUE + " " + + getValueType() + ");"; + DatabaseManager.execSQL(db, sql); + sql = "CREATE UNIQUE INDEX " + getTableName() + "_index ON " + + getTableName() + " " + "(" + Fields.ACCOUNT + ", " + + Fields.USER + ");"; + DatabaseManager.execSQL(db, sql); + } + + /** + * Initial migrate to create table. + * + * @param db + * @param toVersion + * @param tableName + * @param valueType + */ + void initialMigrate(SQLiteDatabase db, String tableName, String valueType) { + String sql; + sql = "CREATE TABLE " + tableName + " (" + "account TEXT," + + "user TEXT," + "value " + valueType + ");"; + DatabaseManager.execSQL(db, sql); + sql = "CREATE UNIQUE INDEX " + tableName + "_index ON " + tableName + + " (account, user);"; + DatabaseManager.execSQL(db, sql); + } + + void write(String account, String user, T value) { + synchronized (writeLock) { + if (writeStatement == null) { + SQLiteDatabase db = databaseManager.getWritableDatabase(); + writeStatement = db.compileStatement("INSERT OR REPLACE INTO " + + getTableName() + " (" + Fields.ACCOUNT + ", " + + Fields.USER + ", " + Fields.VALUE + + ") VALUES (?, ?, ?);"); + } + writeStatement.bindString(1, account); + writeStatement.bindString(2, user); + bindValue(writeStatement, value); + writeStatement.execute(); + } + } } diff --git a/app/src/main/java/com/xabber/android/data/message/chat/ChatInput.java b/app/src/main/java/com/xabber/android/data/message/chat/ChatInput.java index 334a99b174..a69b64a064 100644 --- a/app/src/main/java/com/xabber/android/data/message/chat/ChatInput.java +++ b/app/src/main/java/com/xabber/android/data/message/chat/ChatInput.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,50 +16,49 @@ /** * Represents information about chat input. - * + * * @author alexander.ivanov - * */ class ChatInput { - /** - * Typed text. - */ - private String typedMessage; + /** + * Typed text. + */ + private String typedMessage; - /** - * Start selection position. - */ - private int selectionStart; + /** + * Start selection position. + */ + private int selectionStart; - /** - * End selection position. - */ - private int selectionEnd; + /** + * End selection position. + */ + private int selectionEnd; - public ChatInput() { - typedMessage = ""; - selectionStart = 0; - selectionEnd = 0; - } + public ChatInput() { + typedMessage = ""; + selectionStart = 0; + selectionEnd = 0; + } - public String getTypedMessage() { - return typedMessage; - } + public String getTypedMessage() { + return typedMessage; + } - public int getSelectionStart() { - return selectionStart; - } + public int getSelectionStart() { + return selectionStart; + } - public int getSelectionEnd() { - return selectionEnd; - } + public int getSelectionEnd() { + return selectionEnd; + } - public void setTyped(String typedMessage, int selectionStart, - int selectionEnd) { - this.typedMessage = typedMessage; - this.selectionStart = selectionStart; - this.selectionEnd = selectionEnd; - } + public void setTyped(String typedMessage, int selectionStart, + int selectionEnd) { + this.typedMessage = typedMessage; + this.selectionStart = selectionStart; + this.selectionEnd = selectionEnd; + } } diff --git a/app/src/main/java/com/xabber/android/data/message/chat/ChatManager.java b/app/src/main/java/com/xabber/android/data/message/chat/ChatManager.java index 99443697f2..a9fe3718c9 100644 --- a/app/src/main/java/com/xabber/android/data/message/chat/ChatManager.java +++ b/app/src/main/java/com/xabber/android/data/message/chat/ChatManager.java @@ -1,22 +1,19 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data.message.chat; -import java.util.HashSet; -import java.util.Set; - import android.database.Cursor; import android.net.Uri; @@ -28,361 +25,413 @@ import com.xabber.android.data.entity.BaseEntity; import com.xabber.android.data.entity.NestedMap; +import java.util.HashSet; +import java.util.Set; + /** * Manage chat specific options. - * + * * @author alexander.ivanov - * */ public class ChatManager implements OnLoadListener, OnAccountRemovedListener { - public static final Uri EMPTY_SOUND = Uri - .parse("com.xabber.android.data.message.ChatManager.EMPTY_SOUND"); - - private static final Object PRIVATE_CHAT = new Object(); - - /** - * Stored input for user in account. - */ - private final NestedMap chatInputs; - - /** - * List of chats whose messages mustn't be saved for user in account. - */ - private final NestedMap privateChats; - - /** - * Whether notification in visible chat should be used for user in account. - */ - private final NestedMap notifyVisible; - - /** - * Whether text of incoming message should be shown in notification bar for - * user in account. - */ - private final NestedMap showText; - - /** - * Whether vibro notification should be used for user in account. - */ - private final NestedMap makeVibro; - - /** - * Sound, associated with chat for user in account. - */ - private final NestedMap sounds; - - private final static ChatManager instance; - - static { - instance = new ChatManager(); - Application.getInstance().addManager(instance); - } - - public static ChatManager getInstance() { - return instance; - } - - private ChatManager() { - chatInputs = new NestedMap(); - privateChats = new NestedMap(); - sounds = new NestedMap(); - showText = new NestedMap(); - makeVibro = new NestedMap(); - notifyVisible = new NestedMap(); - } - - @Override - public void onLoad() { - final Set privateChats = new HashSet(); - final NestedMap notifyVisible = new NestedMap(); - final NestedMap showText = new NestedMap(); - final NestedMap makeVibro = new NestedMap(); - final NestedMap sounds = new NestedMap(); - Cursor cursor; - cursor = PrivateChatTable.getInstance().list(); - try { - if (cursor.moveToFirst()) { - do { - privateChats.add(new BaseEntity(PrivateChatTable - .getAccount(cursor), PrivateChatTable - .getUser(cursor))); - } while (cursor.moveToNext()); - } - } finally { - cursor.close(); - } - - cursor = NotifyVisibleTable.getInstance().list(); - try { - if (cursor.moveToFirst()) { - do { - notifyVisible.put(NotifyVisibleTable.getAccount(cursor), - NotifyVisibleTable.getUser(cursor), - NotifyVisibleTable.getValue(cursor)); - } while (cursor.moveToNext()); - } - } finally { - cursor.close(); - } - - cursor = ShowTextTable.getInstance().list(); - try { - if (cursor.moveToFirst()) { - do { - showText.put(ShowTextTable.getAccount(cursor), - ShowTextTable.getUser(cursor), - ShowTextTable.getValue(cursor)); - } while (cursor.moveToNext()); - } - } finally { - cursor.close(); - } - - cursor = VibroTable.getInstance().list(); - try { - if (cursor.moveToFirst()) { - do { - makeVibro.put(VibroTable.getAccount(cursor), - VibroTable.getUser(cursor), - VibroTable.getValue(cursor)); - } while (cursor.moveToNext()); - } - } finally { - cursor.close(); - } - - cursor = SoundTable.getInstance().list(); - try { - if (cursor.moveToFirst()) { - do { - sounds.put(SoundTable.getAccount(cursor), - SoundTable.getUser(cursor), - SoundTable.getValue(cursor)); - } while (cursor.moveToNext()); - } - } finally { - cursor.close(); - } - - Application.getInstance().runOnUiThread(new Runnable() { - @Override - public void run() { - onLoaded(privateChats, notifyVisible, showText, makeVibro, - sounds); - } - }); - } - - private void onLoaded(Set privateChats, - NestedMap notifyVisible, NestedMap showText, - NestedMap vibro, NestedMap sounds) { - for (BaseEntity baseEntity : privateChats) - this.privateChats.put(baseEntity.getAccount(), - baseEntity.getUser(), PRIVATE_CHAT); - this.notifyVisible.addAll(notifyVisible); - this.showText.addAll(showText); - this.makeVibro.addAll(vibro); - this.sounds.addAll(sounds); - } - - @Override - public void onAccountRemoved(AccountItem accountItem) { - chatInputs.clear(accountItem.getAccount()); - privateChats.clear(accountItem.getAccount()); - sounds.clear(accountItem.getAccount()); - showText.clear(accountItem.getAccount()); - makeVibro.clear(accountItem.getAccount()); - notifyVisible.clear(accountItem.getAccount()); - } - - /** - * Whether to save history for specified chat. - * - * @param account - * @param user - * @return - */ - public boolean isSaveMessages(String account, String user) { - return privateChats.get(account, user) != PRIVATE_CHAT; - } - - /** - * Sets whether to save history for specified chat. - * - * @param account - * @param user - * @param save - */ - public void setSaveMessages(final String account, final String user, - final boolean save) { - if (save) - privateChats.remove(account, user); - else - privateChats.put(account, user, PRIVATE_CHAT); - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - if (save) - PrivateChatTable.getInstance().remove(account, user); - else - PrivateChatTable.getInstance().write(account, user); - } - }); - } - - /** - * @param account - * @param user - * @return typed but not sent message. - */ - public String getTypedMessage(String account, String user) { - ChatInput chat = chatInputs.get(account, user); - if (chat == null) - return ""; - return chat.getTypedMessage(); - } - - /** - * @param account - * @param user - * @return Start selection position. - */ - public int getSelectionStart(String account, String user) { - ChatInput chat = chatInputs.get(account, user); - if (chat == null) - return 0; - return chat.getSelectionStart(); - } - - /** - * @param account - * @param user - * @return End selection position. - */ - public int getSelectionEnd(String account, String user) { - ChatInput chat = chatInputs.get(account, user); - if (chat == null) - return 0; - return chat.getSelectionEnd(); - } - - /** - * Sets typed message and selection options for specified chat. - * - * @param account - * @param user - * @param typedMessage - * @param selectionStart - * @param selectionEnd - */ - public void setTyped(String account, String user, String typedMessage, - int selectionStart, int selectionEnd) { - ChatInput chat = chatInputs.get(account, user); - if (chat == null) { - chat = new ChatInput(); - chatInputs.put(account, user, chat); - } - chat.setTyped(typedMessage, selectionStart, selectionEnd); - } - - /** - * @param account - * @param user - * @return Whether notification in visible chat must be shown. Common value - * if there is no user specific value. - */ - public boolean isNotifyVisible(String account, String user) { - Boolean value = notifyVisible.get(account, user); - if (value == null) - return SettingsManager.eventsVisibleChat(); - return value; - } - - public void setNotifyVisible(final String account, final String user, - final boolean value) { - notifyVisible.put(account, user, value); - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - NotifyVisibleTable.getInstance().write(account, user, value); - } - }); - } - - /** - * @param account - * @param user - * @return Whether text of messages must be shown in notification area. - * Common value if there is no user specific value. - */ - public boolean isShowText(String account, String user) { - Boolean value = showText.get(account, user); - if (value == null) - return SettingsManager.eventsShowText(); - return value; - } - - public void setShowText(final String account, final String user, - final boolean value) { - showText.put(account, user, value); - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - ShowTextTable.getInstance().write(account, user, value); - } - }); - } - - /** - * @param account - * @param user - * @return Whether vibro should be used while notification. Common value if - * there is no user specific value. - */ - public boolean isMakeVibro(String account, String user) { - Boolean value = makeVibro.get(account, user); - if (value == null) - return SettingsManager.eventsVibro(); - return value; - } - - public void setMakeVibro(final String account, final String user, - final boolean value) { - makeVibro.put(account, user, value); - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - VibroTable.getInstance().write(account, user, value); - } - }); - } - - /** - * @param account - * @param user - * @return Sound for notification. Common value if there is no user specific - * value. - */ - public Uri getSound(String account, String user) { - Uri value = sounds.get(account, user); - if (value == null) - return SettingsManager.eventsSound(); - if (EMPTY_SOUND.equals(value)) - return null; - return value; - } - - public void setSound(final String account, final String user, - final Uri value) { - sounds.put(account, user, value == null ? EMPTY_SOUND : value); - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - SoundTable.getInstance().write(account, user, - value == null ? EMPTY_SOUND : value); - } - }); - } - + public static final Uri EMPTY_SOUND = Uri + .parse("com.xabber.android.data.message.ChatManager.EMPTY_SOUND"); + + private static final Object PRIVATE_CHAT = new Object(); + private final static ChatManager instance; + + static { + instance = new ChatManager(); + Application.getInstance().addManager(instance); + } + + /** + * Stored input for user in account. + */ + private final NestedMap chatInputs; + /** + * List of chats whose messages mustn't be saved for user in account. + */ + private final NestedMap privateChats; + /** + * Whether notification in visible chat should be used for user in account. + */ + private final NestedMap notifyVisible; + /** + * Whether text of incoming message should be shown in notification bar for + * user in account. + */ + private final NestedMap showText; + /** + * Whether vibro notification should be used for user in account. + */ + private final NestedMap makeVibro; + /** + * Sound, associated with chat for user in account. + */ + private final NestedMap sounds; + /** + * Whether 'This room is not anonymous'-messages (Status Code 100) should be suppressed + */ + private final NestedMap suppress100; + + private ChatManager() { + chatInputs = new NestedMap(); + privateChats = new NestedMap(); + sounds = new NestedMap(); + showText = new NestedMap<>(); + makeVibro = new NestedMap(); + notifyVisible = new NestedMap(); + suppress100 = new NestedMap(); + } + + public static ChatManager getInstance() { + return instance; + } + + @Override + public void onLoad() { + final Set privateChats = new HashSet(); + final NestedMap notifyVisible = new NestedMap(); + final NestedMap showText = new NestedMap<>(); + final NestedMap makeVibro = new NestedMap(); + final NestedMap sounds = new NestedMap(); + final NestedMap suppress100 = new NestedMap(); + Cursor cursor; + cursor = PrivateChatTable.getInstance().list(); + try { + if (cursor.moveToFirst()) { + do { + privateChats.add(new BaseEntity(PrivateChatTable + .getAccount(cursor), PrivateChatTable + .getUser(cursor))); + } while (cursor.moveToNext()); + } + } finally { + cursor.close(); + } + + cursor = NotifyVisibleTable.getInstance().list(); + try { + if (cursor.moveToFirst()) { + do { + notifyVisible.put(NotifyVisibleTable.getAccount(cursor), + NotifyVisibleTable.getUser(cursor), + NotifyVisibleTable.getValue(cursor)); + } while (cursor.moveToNext()); + } + } finally { + cursor.close(); + } + + cursor = ShowTextTable.getInstance().list(); + try { + if (cursor.moveToFirst()) { + do { + showText.put(ShowTextTable.getAccount(cursor), + ShowTextTable.getUser(cursor), + ShowTextTable.getValue(cursor)); + } while (cursor.moveToNext()); + } + } finally { + cursor.close(); + } + + cursor = VibroTable.getInstance().list(); + try { + if (cursor.moveToFirst()) { + do { + makeVibro.put(VibroTable.getAccount(cursor), + VibroTable.getUser(cursor), + VibroTable.getValue(cursor)); + } while (cursor.moveToNext()); + } + } finally { + cursor.close(); + } + + cursor = SoundTable.getInstance().list(); + try { + if (cursor.moveToFirst()) { + do { + sounds.put(SoundTable.getAccount(cursor), + SoundTable.getUser(cursor), + SoundTable.getValue(cursor)); + } while (cursor.moveToNext()); + } + } finally { + cursor.close(); + } + + cursor = Suppress100Table.getInstance().list(); + try { + if (cursor.moveToFirst()) { + do { + makeVibro.put(Suppress100Table.getAccount(cursor), + Suppress100Table.getUser(cursor), + Suppress100Table.getValue(cursor)); + } while (cursor.moveToNext()); + } + } finally { + cursor.close(); + } + + Application.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + onLoaded(privateChats, notifyVisible, showText, makeVibro, + sounds, suppress100); + } + }); + } + + private void onLoaded(Set privateChats, + NestedMap notifyVisible, NestedMap showText, + NestedMap vibro, NestedMap sounds, NestedMap suppress100) { + for (BaseEntity baseEntity : privateChats) + this.privateChats.put(baseEntity.getAccount(), + baseEntity.getUser(), PRIVATE_CHAT); + this.notifyVisible.addAll(notifyVisible); + this.showText.addAll(showText); + this.makeVibro.addAll(vibro); + this.sounds.addAll(sounds); + this.suppress100.addAll(suppress100); + } + + @Override + public void onAccountRemoved(AccountItem accountItem) { + chatInputs.clear(accountItem.getAccount()); + privateChats.clear(accountItem.getAccount()); + sounds.clear(accountItem.getAccount()); + showText.clear(accountItem.getAccount()); + makeVibro.clear(accountItem.getAccount()); + notifyVisible.clear(accountItem.getAccount()); + suppress100.clear(accountItem.getAccount()); + } + + /** + * Whether to save history for specified chat. + * + * @param account + * @param user + * @return + */ + public boolean isSaveMessages(String account, String user) { + return privateChats.get(account, user) != PRIVATE_CHAT; + } + + /** + * Sets whether to save history for specified chat. + * + * @param account + * @param user + * @param save + */ + public void setSaveMessages(final String account, final String user, + final boolean save) { + if (save) + privateChats.remove(account, user); + else + privateChats.put(account, user, PRIVATE_CHAT); + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + if (save) + PrivateChatTable.getInstance().remove(account, user); + else + PrivateChatTable.getInstance().write(account, user); + } + }); + } + + /** + * @param account + * @param user + * @return typed but not sent message. + */ + public String getTypedMessage(String account, String user) { + ChatInput chat = chatInputs.get(account, user); + if (chat == null) + return ""; + return chat.getTypedMessage(); + } + + /** + * @param account + * @param user + * @return Start selection position. + */ + public int getSelectionStart(String account, String user) { + ChatInput chat = chatInputs.get(account, user); + if (chat == null) + return 0; + return chat.getSelectionStart(); + } + + /** + * @param account + * @param user + * @return End selection position. + */ + public int getSelectionEnd(String account, String user) { + ChatInput chat = chatInputs.get(account, user); + if (chat == null) + return 0; + return chat.getSelectionEnd(); + } + + /** + * Sets typed message and selection options for specified chat. + * + * @param account + * @param user + * @param typedMessage + * @param selectionStart + * @param selectionEnd + */ + public void setTyped(String account, String user, String typedMessage, + int selectionStart, int selectionEnd) { + ChatInput chat = chatInputs.get(account, user); + if (chat == null) { + chat = new ChatInput(); + chatInputs.put(account, user, chat); + } + chat.setTyped(typedMessage, selectionStart, selectionEnd); + } + + /** + * @param account + * @param user + * @return Whether notification in visible chat must be shown. Common value + * if there is no user specific value. + */ + public boolean isNotifyVisible(String account, String user) { + Boolean value = notifyVisible.get(account, user); + if (value == null) + return SettingsManager.eventsVisibleChat(); + return value; + } + + public void setNotifyVisible(final String account, final String user, + final boolean value) { + notifyVisible.put(account, user, value); + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + NotifyVisibleTable.getInstance().write(account, user, value); + } + }); + } + + /** + * @param account + * @param user + * @return Whether text of messages must be shown in notification area. + * Common value if there is no user specific value. + */ + public boolean isShowText(String account, String user) { + switch (getShowText(account, user)) { + case show: + return true; + case hide: + return false; + case default_settings: + default: + return SettingsManager.eventsShowText(); + } + } + + public ShowMessageTextInNotification getShowText(String account, String user) { + ShowMessageTextInNotification showMessageTextInNotification = showText.get(account, user); + if (showMessageTextInNotification == null) { + return ShowMessageTextInNotification.default_settings; + } else { + return showMessageTextInNotification; + } + } + + public void setShowText(final String account, final String user, final ShowMessageTextInNotification value) { + showText.put(account, user, value); + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + ShowTextTable.getInstance().write(account, user, value); + } + }); + } + + /** + * @param account + * @param user + * @return Whether vibro should be used while notification. Common value if + * there is no user specific value. + */ + public boolean isMakeVibro(String account, String user) { + Boolean value = makeVibro.get(account, user); + if (value == null) + return SettingsManager.eventsVibro(); + return value; + } + + public void setMakeVibro(final String account, final String user, + final boolean value) { + makeVibro.put(account, user, value); + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + VibroTable.getInstance().write(account, user, value); + } + }); + } + + /** + * @param account + * @param user + * @return Sound for notification. Common value if there is no user specific + * value. + */ + public Uri getSound(String account, String user) { + Uri value = sounds.get(account, user); + if (value == null) + return SettingsManager.eventsSound(); + if (EMPTY_SOUND.equals(value)) + return null; + return value; + } + + public void setSound(final String account, final String user, + final Uri value) { + sounds.put(account, user, value == null ? EMPTY_SOUND : value); + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + SoundTable.getInstance().write(account, user, + value == null ? EMPTY_SOUND : value); + } + }); + } + + /** + * @param account + * @param user + * @return Whether 'This Room is not Anonymous'-messages (Status Code 100) should be suppressed. + */ + public boolean isSuppress100(String account, String user) { + Boolean value = suppress100.get(account, user); + if (value == null) + return SettingsManager.eventsSuppress100(); + return value; + } + + public void setSuppress100(final String account, final String user, + final boolean value) { + suppress100.put(account, user, value); + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + Suppress100Table.getInstance().write(account, user, value); + } + }); + } } diff --git a/app/src/main/java/com/xabber/android/data/message/chat/NotifyVisibleTable.java b/app/src/main/java/com/xabber/android/data/message/chat/NotifyVisibleTable.java index 5e0acb840e..36836e225e 100644 --- a/app/src/main/java/com/xabber/android/data/message/chat/NotifyVisibleTable.java +++ b/app/src/main/java/com/xabber/android/data/message/chat/NotifyVisibleTable.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -22,58 +22,57 @@ /** * Storage with settings to notify about messages in visible chat. - * + * * @author alexander.ivanov - * */ class NotifyVisibleTable extends AbstractChatPropertyTable { - static final String NAME = "chat_notify_visible"; + static final String NAME = "chat_notify_visible"; - private final static NotifyVisibleTable instance; + private final static NotifyVisibleTable instance; - static { - instance = new NotifyVisibleTable(DatabaseManager.getInstance()); - DatabaseManager.getInstance().addTable(instance); - } + static { + instance = new NotifyVisibleTable(DatabaseManager.getInstance()); + DatabaseManager.getInstance().addTable(instance); + } - public static NotifyVisibleTable getInstance() { - return instance; - } + public static NotifyVisibleTable getInstance() { + return instance; + } - private NotifyVisibleTable(DatabaseManager databaseManager) { - super(databaseManager); - } + private NotifyVisibleTable(DatabaseManager databaseManager) { + super(databaseManager); + } - @Override - protected String getTableName() { - return NAME; - } + @Override + protected String getTableName() { + return NAME; + } - @Override - String getValueType() { - return "INTEGER"; - } + @Override + String getValueType() { + return "INTEGER"; + } - @Override - void bindValue(SQLiteStatement writeStatement, Boolean value) { - writeStatement.bindLong(3, value ? 1 : 0); - } + @Override + void bindValue(SQLiteStatement writeStatement, Boolean value) { + writeStatement.bindLong(3, value ? 1 : 0); + } - @Override - public void migrate(SQLiteDatabase db, int toVersion) { - super.migrate(db, toVersion); - switch (toVersion) { - case 52: - initialMigrate(db, "chat_notify_visible", "INTEGER"); - break; - default: - break; - } - } + @Override + public void migrate(SQLiteDatabase db, int toVersion) { + super.migrate(db, toVersion); + switch (toVersion) { + case 52: + initialMigrate(db, "chat_notify_visible", "INTEGER"); + break; + default: + break; + } + } - static boolean getValue(Cursor cursor) { - return cursor.getLong(cursor.getColumnIndex(Fields.VALUE)) != 0; - } + static boolean getValue(Cursor cursor) { + return cursor.getLong(cursor.getColumnIndex(Fields.VALUE)) != 0; + } } diff --git a/app/src/main/java/com/xabber/android/data/message/chat/PrivateChatTable.java b/app/src/main/java/com/xabber/android/data/message/chat/PrivateChatTable.java index 895c84cef7..324bc59a30 100644 --- a/app/src/main/java/com/xabber/android/data/message/chat/PrivateChatTable.java +++ b/app/src/main/java/com/xabber/android/data/message/chat/PrivateChatTable.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -22,137 +22,136 @@ /** * Storage for chats with disabled history. - * + * * @author alexander.ivanov - * */ class PrivateChatTable extends AbstractEntityTable { - static final class Fields implements AbstractEntityTable.Fields { - - private Fields() { - } - - } - - static final String NAME = "private_chats"; - private static final String[] PROJECTION = new String[] { Fields.ACCOUNT, - Fields.USER }; - - private final DatabaseManager databaseManager; - private SQLiteStatement writeStatement; - private final Object writeLock; - - private final static PrivateChatTable instance; - - static { - instance = new PrivateChatTable(DatabaseManager.getInstance()); - DatabaseManager.getInstance().addTable(instance); - } - - public static PrivateChatTable getInstance() { - return instance; - } - - private PrivateChatTable(DatabaseManager databaseManager) { - this.databaseManager = databaseManager; - writeStatement = null; - writeLock = new Object(); - } - - @Override - public void create(SQLiteDatabase db) { - String sql = "CREATE TABLE " + NAME + " (" + Fields.ACCOUNT + " TEXT," - + Fields.USER + " TEXT);"; - DatabaseManager.execSQL(db, sql); - sql = "CREATE UNIQUE INDEX " + NAME + "_index ON " + NAME + " " + "(" - + Fields.ACCOUNT + ", " + Fields.USER + ");"; - DatabaseManager.execSQL(db, sql); - } - - @Override - public void migrate(SQLiteDatabase db, int toVersion) { - super.migrate(db, toVersion); - String sql; - switch (toVersion) { - case 16: - sql = "CREATE TABLE chats (" + "account TEXT," + "user TEXT," - + "save BOOLEAN);"; - DatabaseManager.execSQL(db, sql); - sql = "CREATE UNIQUE INDEX chats_index ON chats " - + "(account, user);"; - DatabaseManager.execSQL(db, sql); - break; - case 18: - sql = "ALTER TABLE chats ADD COLUMN " + "message TEXT;"; - DatabaseManager.execSQL(db, sql); - sql = "UPDATE chats SET message = \"\";"; - DatabaseManager.execSQL(db, sql); - break; - case 19: - sql = "UPDATE chats SET message = \"\";"; - DatabaseManager.execSQL(db, sql); - break; - case 20: - DatabaseManager.dropTable(db, "chats"); - sql = "CREATE TABLE chats (" + "account TEXT," + "user TEXT," - + "save_messages BOOLEAN, typed_message TEXT);"; - DatabaseManager.execSQL(db, sql); - sql = "CREATE UNIQUE INDEX chats_index ON chats " - + "(account, user);"; - DatabaseManager.execSQL(db, sql); - break; - case 26: - sql = "CREATE TABLE private_chats (" + "account TEXT," - + "user TEXT);"; - DatabaseManager.execSQL(db, sql); - sql = "CREATE UNIQUE INDEX private_chats_index ON private_chats " - + "(account, user);"; - DatabaseManager.execSQL(db, sql); - sql = "INSERT INTO private_chats (account, user) " - + "SELECT account, user FROM chats WHERE NOT save_messages;"; - DatabaseManager.execSQL(db, sql); - DatabaseManager.dropTable(db, "chats"); - break; - default: - break; - } - } - - /** - * Sets that messages for specified chat must not to be saved. - */ - void write(String account, String user) { - synchronized (writeLock) { - if (writeStatement == null) { - SQLiteDatabase db = databaseManager.getWritableDatabase(); - writeStatement = db.compileStatement("INSERT OR REPLACE INTO " - + NAME + " (" + Fields.ACCOUNT + ", " + Fields.USER - + ") VALUES (?, ?);"); - } - writeStatement.bindString(1, account); - writeStatement.bindString(2, user); - writeStatement.execute(); - } - } - - /** - * Sets that messages for specified chat can be saved. - */ - void remove(String account, String user) { - SQLiteDatabase db = databaseManager.getWritableDatabase(); - db.delete(NAME, Fields.ACCOUNT + " = ? AND " + Fields.USER + " = ?", - new String[] { account, user }); - } - - @Override - protected String getTableName() { - return NAME; - } - - @Override - protected String[] getProjection() { - return PROJECTION; - } + static final class Fields implements AbstractEntityTable.Fields { + + private Fields() { + } + + } + + static final String NAME = "private_chats"; + private static final String[] PROJECTION = new String[]{Fields.ACCOUNT, + Fields.USER}; + + private final DatabaseManager databaseManager; + private SQLiteStatement writeStatement; + private final Object writeLock; + + private final static PrivateChatTable instance; + + static { + instance = new PrivateChatTable(DatabaseManager.getInstance()); + DatabaseManager.getInstance().addTable(instance); + } + + public static PrivateChatTable getInstance() { + return instance; + } + + private PrivateChatTable(DatabaseManager databaseManager) { + this.databaseManager = databaseManager; + writeStatement = null; + writeLock = new Object(); + } + + @Override + public void create(SQLiteDatabase db) { + String sql = "CREATE TABLE " + NAME + " (" + Fields.ACCOUNT + " TEXT," + + Fields.USER + " TEXT);"; + DatabaseManager.execSQL(db, sql); + sql = "CREATE UNIQUE INDEX " + NAME + "_index ON " + NAME + " " + "(" + + Fields.ACCOUNT + ", " + Fields.USER + ");"; + DatabaseManager.execSQL(db, sql); + } + + @Override + public void migrate(SQLiteDatabase db, int toVersion) { + super.migrate(db, toVersion); + String sql; + switch (toVersion) { + case 16: + sql = "CREATE TABLE chats (" + "account TEXT," + "user TEXT," + + "save BOOLEAN);"; + DatabaseManager.execSQL(db, sql); + sql = "CREATE UNIQUE INDEX chats_index ON chats " + + "(account, user);"; + DatabaseManager.execSQL(db, sql); + break; + case 18: + sql = "ALTER TABLE chats ADD COLUMN " + "message TEXT;"; + DatabaseManager.execSQL(db, sql); + sql = "UPDATE chats SET message = \"\";"; + DatabaseManager.execSQL(db, sql); + break; + case 19: + sql = "UPDATE chats SET message = \"\";"; + DatabaseManager.execSQL(db, sql); + break; + case 20: + DatabaseManager.dropTable(db, "chats"); + sql = "CREATE TABLE chats (" + "account TEXT," + "user TEXT," + + "save_messages BOOLEAN, typed_message TEXT);"; + DatabaseManager.execSQL(db, sql); + sql = "CREATE UNIQUE INDEX chats_index ON chats " + + "(account, user);"; + DatabaseManager.execSQL(db, sql); + break; + case 26: + sql = "CREATE TABLE private_chats (" + "account TEXT," + + "user TEXT);"; + DatabaseManager.execSQL(db, sql); + sql = "CREATE UNIQUE INDEX private_chats_index ON private_chats " + + "(account, user);"; + DatabaseManager.execSQL(db, sql); + sql = "INSERT INTO private_chats (account, user) " + + "SELECT account, user FROM chats WHERE NOT save_messages;"; + DatabaseManager.execSQL(db, sql); + DatabaseManager.dropTable(db, "chats"); + break; + default: + break; + } + } + + /** + * Sets that messages for specified chat must not to be saved. + */ + void write(String account, String user) { + synchronized (writeLock) { + if (writeStatement == null) { + SQLiteDatabase db = databaseManager.getWritableDatabase(); + writeStatement = db.compileStatement("INSERT OR REPLACE INTO " + + NAME + " (" + Fields.ACCOUNT + ", " + Fields.USER + + ") VALUES (?, ?);"); + } + writeStatement.bindString(1, account); + writeStatement.bindString(2, user); + writeStatement.execute(); + } + } + + /** + * Sets that messages for specified chat can be saved. + */ + void remove(String account, String user) { + SQLiteDatabase db = databaseManager.getWritableDatabase(); + db.delete(NAME, Fields.ACCOUNT + " = ? AND " + Fields.USER + " = ?", + new String[]{account, user}); + } + + @Override + protected String getTableName() { + return NAME; + } + + @Override + protected String[] getProjection() { + return PROJECTION; + } } diff --git a/app/src/main/java/com/xabber/android/data/message/chat/ShowMessageTextInNotification.java b/app/src/main/java/com/xabber/android/data/message/chat/ShowMessageTextInNotification.java new file mode 100644 index 0000000000..9b71818d05 --- /dev/null +++ b/app/src/main/java/com/xabber/android/data/message/chat/ShowMessageTextInNotification.java @@ -0,0 +1,34 @@ +package com.xabber.android.data.message.chat; + + +public enum ShowMessageTextInNotification { + + /** + * Show message text in notifications according to global settings. + */ + default_settings, + + /** + * Always show message text in notifications. + */ + show, + + /** + * Never show message text in notifications. + */ + hide; + + public static ShowMessageTextInNotification fromInteger(int x) { + switch (x) { + case 0: + return default_settings; + case 1: + return show; + case 2: + return hide; + default: + return default_settings; + } + } + +} diff --git a/app/src/main/java/com/xabber/android/data/message/chat/ShowTextTable.java b/app/src/main/java/com/xabber/android/data/message/chat/ShowTextTable.java index d0bd29f1eb..4fc4abc2a6 100644 --- a/app/src/main/java/com/xabber/android/data/message/chat/ShowTextTable.java +++ b/app/src/main/java/com/xabber/android/data/message/chat/ShowTextTable.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -19,61 +19,81 @@ import android.database.sqlite.SQLiteStatement; import com.xabber.android.data.DatabaseManager; +import com.xabber.android.data.SettingsManager; /** * Storage with settings to show text in notification for each chat. - * + * * @author alexander.ivanov - * */ -class ShowTextTable extends AbstractChatPropertyTable { - - static final String NAME = "chat_show_text"; - - private final static ShowTextTable instance; - - static { - instance = new ShowTextTable(DatabaseManager.getInstance()); - DatabaseManager.getInstance().addTable(instance); - } - - public static ShowTextTable getInstance() { - return instance; - } - - private ShowTextTable(DatabaseManager databaseManager) { - super(databaseManager); - } - - @Override - protected String getTableName() { - return NAME; - } - - @Override - String getValueType() { - return "INTEGER"; - } - - @Override - void bindValue(SQLiteStatement writeStatement, Boolean value) { - writeStatement.bindLong(3, value ? 1 : 0); - } - - @Override - public void migrate(SQLiteDatabase db, int toVersion) { - super.migrate(db, toVersion); - switch (toVersion) { - case 52: - initialMigrate(db, "chat_show_text", "INTEGER"); - break; - default: - break; - } - } - - static boolean getValue(Cursor cursor) { - return cursor.getLong(cursor.getColumnIndex(Fields.VALUE)) != 0; - } +class ShowTextTable extends AbstractChatPropertyTable { + + static final String NAME = "chat_show_text"; + + private final static ShowTextTable instance; + + static { + instance = new ShowTextTable(DatabaseManager.getInstance()); + DatabaseManager.getInstance().addTable(instance); + } + + private ShowTextTable(DatabaseManager databaseManager) { + super(databaseManager); + } + + public static ShowTextTable getInstance() { + return instance; + } + + static ShowMessageTextInNotification getValue(Cursor cursor) { + return ShowMessageTextInNotification.fromInteger((int) cursor.getLong(cursor.getColumnIndex(Fields.VALUE))); + } + + @Override + protected String getTableName() { + return NAME; + } + + @Override + String getValueType() { + return "INTEGER"; + } + + @Override + void bindValue(SQLiteStatement writeStatement, ShowMessageTextInNotification showMessageTextInNotification) { + writeStatement.bindLong(3, showMessageTextInNotification.ordinal()); + } + + @Override + public void migrate(SQLiteDatabase db, int toVersion) { + super.migrate(db, toVersion); + switch (toVersion) { + case 52: + initialMigrate(db, "chat_show_text", "INTEGER"); + break; + + case 67: + int trueMigrationValue; + int falseMigrationValue; + + if (SettingsManager.eventsShowText()) { + trueMigrationValue = ShowMessageTextInNotification.default_settings.ordinal(); + falseMigrationValue = ShowMessageTextInNotification.hide.ordinal(); + } else { + trueMigrationValue = ShowMessageTextInNotification.show.ordinal(); + falseMigrationValue = ShowMessageTextInNotification.default_settings.ordinal(); + } + + + String sql = "UPDATE " + NAME + + " SET " + Fields.VALUE + " = CASE WHEN (" + Fields.VALUE + "=1) THEN " + + trueMigrationValue + " ELSE " + falseMigrationValue + " END;"; + + DatabaseManager.execSQL(db, sql); + + default: + break; + } + } } diff --git a/app/src/main/java/com/xabber/android/data/message/chat/SoundTable.java b/app/src/main/java/com/xabber/android/data/message/chat/SoundTable.java index fe80ecbeb1..3b8e5ac704 100644 --- a/app/src/main/java/com/xabber/android/data/message/chat/SoundTable.java +++ b/app/src/main/java/com/xabber/android/data/message/chat/SoundTable.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -23,58 +23,57 @@ /** * Storage with sound associated with chat. - * + * * @author alexander.ivanov - * */ class SoundTable extends AbstractChatPropertyTable { - static final String NAME = "chat_sound"; + static final String NAME = "chat_sound"; - private final static SoundTable instance; + private final static SoundTable instance; - static { - instance = new SoundTable(DatabaseManager.getInstance()); - DatabaseManager.getInstance().addTable(instance); - } + static { + instance = new SoundTable(DatabaseManager.getInstance()); + DatabaseManager.getInstance().addTable(instance); + } - public static SoundTable getInstance() { - return instance; - } + public static SoundTable getInstance() { + return instance; + } - private SoundTable(DatabaseManager databaseManager) { - super(databaseManager); - } + private SoundTable(DatabaseManager databaseManager) { + super(databaseManager); + } - @Override - protected String getTableName() { - return NAME; - } + @Override + protected String getTableName() { + return NAME; + } - @Override - String getValueType() { - return "TEXT"; - } + @Override + String getValueType() { + return "TEXT"; + } - @Override - void bindValue(SQLiteStatement writeStatement, Uri value) { - writeStatement.bindString(3, value.toString()); - } + @Override + void bindValue(SQLiteStatement writeStatement, Uri value) { + writeStatement.bindString(3, value.toString()); + } - @Override - public void migrate(SQLiteDatabase db, int toVersion) { - super.migrate(db, toVersion); - switch (toVersion) { - case 52: - initialMigrate(db, "chat_sound", "TEXT"); - break; - default: - break; - } - } + @Override + public void migrate(SQLiteDatabase db, int toVersion) { + super.migrate(db, toVersion); + switch (toVersion) { + case 52: + initialMigrate(db, "chat_sound", "TEXT"); + break; + default: + break; + } + } - static Uri getValue(Cursor cursor) { - return Uri.parse(cursor.getString(cursor.getColumnIndex(Fields.VALUE))); - } + static Uri getValue(Cursor cursor) { + return Uri.parse(cursor.getString(cursor.getColumnIndex(Fields.VALUE))); + } } diff --git a/app/src/main/java/com/xabber/android/data/message/chat/Suppress100Table.java b/app/src/main/java/com/xabber/android/data/message/chat/Suppress100Table.java new file mode 100644 index 0000000000..3b3ffd75f4 --- /dev/null +++ b/app/src/main/java/com/xabber/android/data/message/chat/Suppress100Table.java @@ -0,0 +1,62 @@ +package com.xabber.android.data.message.chat; + +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteStatement; +import com.xabber.android.data.DatabaseManager; + +/** + * Storage with suppress100 settings for each chat. + * @author Jaro Fietz. + */ +class Suppress100Table extends AbstractChatPropertyTable { + + static final String NAME = "chat_suppress_100"; + + private final static Suppress100Table instance; + + static { + instance = new Suppress100Table(DatabaseManager.getInstance()); + DatabaseManager.getInstance().addTable(instance); + } + + public static Suppress100Table getInstance() { + return instance; + } + + private Suppress100Table(DatabaseManager databaseManager) { + super(databaseManager); + } + + @Override + protected String getTableName() { + return NAME; + } + + @Override + String getValueType() { + return "INTEGER"; + } + + @Override + void bindValue(SQLiteStatement writeStatement, Boolean value) { + writeStatement.bindLong(3, value ? 1 : 0); + } + + @Override + public void migrate(SQLiteDatabase db, int toVersion) { + super.migrate(db, toVersion); + switch (toVersion) { + case 68: + initialMigrate(db, NAME, "INTEGER"); + break; + default: + break; + } + } + + static boolean getValue(Cursor cursor) { + return cursor.getLong(cursor.getColumnIndex(Fields.VALUE)) != 0; + } + +} diff --git a/app/src/main/java/com/xabber/android/data/message/chat/VibroTable.java b/app/src/main/java/com/xabber/android/data/message/chat/VibroTable.java index 1675fd5514..eee4ed0c65 100644 --- a/app/src/main/java/com/xabber/android/data/message/chat/VibroTable.java +++ b/app/src/main/java/com/xabber/android/data/message/chat/VibroTable.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -22,58 +22,57 @@ /** * Storage with vibro settings for each chat. - * + * * @author alexander.ivanov - * */ class VibroTable extends AbstractChatPropertyTable { - static final String NAME = "chat_vibro"; + static final String NAME = "chat_vibro"; - private final static VibroTable instance; + private final static VibroTable instance; - static { - instance = new VibroTable(DatabaseManager.getInstance()); - DatabaseManager.getInstance().addTable(instance); - } + static { + instance = new VibroTable(DatabaseManager.getInstance()); + DatabaseManager.getInstance().addTable(instance); + } - public static VibroTable getInstance() { - return instance; - } + public static VibroTable getInstance() { + return instance; + } - private VibroTable(DatabaseManager databaseManager) { - super(databaseManager); - } + private VibroTable(DatabaseManager databaseManager) { + super(databaseManager); + } - @Override - protected String getTableName() { - return NAME; - } + @Override + protected String getTableName() { + return NAME; + } - @Override - String getValueType() { - return "INTEGER"; - } + @Override + String getValueType() { + return "INTEGER"; + } - @Override - void bindValue(SQLiteStatement writeStatement, Boolean value) { - writeStatement.bindLong(3, value ? 1 : 0); - } + @Override + void bindValue(SQLiteStatement writeStatement, Boolean value) { + writeStatement.bindLong(3, value ? 1 : 0); + } - @Override - public void migrate(SQLiteDatabase db, int toVersion) { - super.migrate(db, toVersion); - switch (toVersion) { - case 57: - initialMigrate(db, "chat_vibro", "INTEGER"); - break; - default: - break; - } - } + @Override + public void migrate(SQLiteDatabase db, int toVersion) { + super.migrate(db, toVersion); + switch (toVersion) { + case 57: + initialMigrate(db, "chat_vibro", "INTEGER"); + break; + default: + break; + } + } - static boolean getValue(Cursor cursor) { - return cursor.getLong(cursor.getColumnIndex(Fields.VALUE)) != 0; - } + static boolean getValue(Cursor cursor) { + return cursor.getLong(cursor.getColumnIndex(Fields.VALUE)) != 0; + } } diff --git a/app/src/main/java/com/xabber/android/data/message/phrase/Phrase.java b/app/src/main/java/com/xabber/android/data/message/phrase/Phrase.java index b1627a39a7..eb567be3ed 100644 --- a/app/src/main/java/com/xabber/android/data/message/phrase/Phrase.java +++ b/app/src/main/java/com/xabber/android/data/message/phrase/Phrase.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -22,113 +22,112 @@ /** * Settings for phrase search. - * + * * @author alexander.ivanov - * */ public class Phrase { - /** - * Database ID. Should be used from background thread only. - */ - private Long id; - - /** - * Part of message body. - */ - private String text; - - /** - * Part of sender's JID. - */ - private String user; - - /** - * Part of one of the sender's roster groups. - */ - private String group; - - private boolean regexp; - private Uri sound; - - private Pattern textPattern; - private Pattern userPattern; - private Pattern groupPattern; - - public Phrase(Long id, String value, String user, String group, - boolean regexp, Uri sound) { - super(); - setId(id); - update(value, user, group, regexp, sound); - } - - /** - * @param text - * @param user - * @param groups - * @return Whether phrase was found in specified text for user in specified - * groups. - */ - public boolean matches(String text, String user, Collection groups) { - if (textPattern.matcher(text).find() - && userPattern.matcher(user).find()) { - if (groups.isEmpty()) - return groupPattern.matcher("").find(); - for (String group : groups) - if (groupPattern.matcher(group).find()) - return true; - } - return false; - } - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getText() { - return text; - } - - public String getUser() { - return user; - } - - public String getGroup() { - return group; - } - - public boolean isRegexp() { - return regexp; - } - - public Uri getSound() { - return sound; - } - - void update(String text, String user, String group, boolean regexp, - Uri sound) { - this.text = text; - this.user = user; - this.group = group; - this.regexp = regexp; - this.sound = sound; - if (!regexp) { - text = Pattern.quote(text); - user = Pattern.quote(user); - group = Pattern.quote(group); - } - textPattern = compile(text); - userPattern = compile(user); - groupPattern = compile(group); - } - - public static Pattern compile(String value) throws PatternSyntaxException { - return Pattern.compile(value, Pattern.CASE_INSENSITIVE - | Pattern.MULTILINE); - } + /** + * Database ID. Should be used from background thread only. + */ + private Long id; + + /** + * Part of message body. + */ + private String text; + + /** + * Part of sender's JID. + */ + private String user; + + /** + * Part of one of the sender's roster groups. + */ + private String group; + + private boolean regexp; + private Uri sound; + + private Pattern textPattern; + private Pattern userPattern; + private Pattern groupPattern; + + public Phrase(Long id, String value, String user, String group, + boolean regexp, Uri sound) { + super(); + setId(id); + update(value, user, group, regexp, sound); + } + + /** + * @param text + * @param user + * @param groups + * @return Whether phrase was found in specified text for user in specified + * groups. + */ + public boolean matches(String text, String user, Collection groups) { + if (textPattern.matcher(text).find() + && userPattern.matcher(user).find()) { + if (groups.isEmpty()) + return groupPattern.matcher("").find(); + for (String group : groups) + if (groupPattern.matcher(group).find()) + return true; + } + return false; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getText() { + return text; + } + + public String getUser() { + return user; + } + + public String getGroup() { + return group; + } + + public boolean isRegexp() { + return regexp; + } + + public Uri getSound() { + return sound; + } + + void update(String text, String user, String group, boolean regexp, + Uri sound) { + this.text = text; + this.user = user; + this.group = group; + this.regexp = regexp; + this.sound = sound; + if (!regexp) { + text = Pattern.quote(text); + user = Pattern.quote(user); + group = Pattern.quote(group); + } + textPattern = compile(text); + userPattern = compile(user); + groupPattern = compile(group); + } + + public static Pattern compile(String value) throws PatternSyntaxException { + return Pattern.compile(value, Pattern.CASE_INSENSITIVE + | Pattern.MULTILINE); + } } diff --git a/app/src/main/java/com/xabber/android/data/message/phrase/PhraseManager.java b/app/src/main/java/com/xabber/android/data/message/phrase/PhraseManager.java index 15809edc8f..e92b231043 100644 --- a/app/src/main/java/com/xabber/android/data/message/phrase/PhraseManager.java +++ b/app/src/main/java/com/xabber/android/data/message/phrase/PhraseManager.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -28,134 +28,132 @@ /** * Manage custom notification based on message. - * + * * @author alexander.ivanov - * */ public class PhraseManager implements OnLoadListener { - /** - * List of settings. - */ - private final List phrases; - - private final static PhraseManager instance; - - static { - instance = new PhraseManager(); - Application.getInstance().addManager(instance); - } - - public static PhraseManager getInstance() { - return instance; - } - - private PhraseManager() { - phrases = new ArrayList(); - } - - @Override - public void onLoad() { - final Collection phrases = new ArrayList(); - Cursor cursor; - cursor = PhraseTable.getInstance().list(); - try { - if (cursor.moveToFirst()) { - do { - phrases.add(new Phrase(PhraseTable.getId(cursor), - PhraseTable.getValue(cursor), PhraseTable - .getUser(cursor), PhraseTable - .getGroup(cursor), PhraseTable - .isRegexp(cursor), PhraseTable - .getSound(cursor))); - } while (cursor.moveToNext()); - } - } finally { - cursor.close(); - } - Application.getInstance().runOnUiThread(new Runnable() { - @Override - public void run() { - onLoaded(phrases); - } - }); - } - - private void onLoaded(Collection phrases) { - this.phrases.addAll(phrases); - } - - /** - * @param text - * @return Sound associated with first matched phrase. Chat specific setting - * if no one matches . - */ - public Uri getSound(String account, String user, String text) { - Collection groups = RosterManager.getInstance().getGroups( - account, user); - for (Phrase phrase : phrases) - if (phrase.matches(text, user, groups)) { - Uri value = phrase.getSound(); - if (ChatManager.EMPTY_SOUND.equals(value)) - return null; - return value; - } - return ChatManager.getInstance().getSound(account, user); - } - - /** - * Update phrase or create. - * - * @param phrase - * can be null for new phrase. - * @param value - * @param regexp - * @param sound - */ - public void updateOrCreatePhrase(Phrase phrase, String value, String user, - String group, boolean regexp, Uri sound) { - if (phrase == null) { - phrase = new Phrase(null, value, user, group, regexp, sound); - phrases.add(phrase); - } else { - phrase.update(value, user, group, regexp, sound); - } - writePhrase(phrase, value, user, group, regexp, sound); - } - - /** - * Removes phrase. - * - * @param index - */ - public void removePhrase(int index) { - phrases.remove(getPhrase(index)); - } - - private void writePhrase(final Phrase phrase, final String value, - final String user, final String group, final boolean regexp, - final Uri sound) { - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - phrase.setId(PhraseTable.getInstance().write(phrase.getId(), - value, user, group, regexp, - sound == null ? ChatManager.EMPTY_SOUND : sound)); - } - }); - } - - public Collection getPhrases() { - Collection collection = new ArrayList(); - for (int index = 0; index < phrases.size(); index++) - collection.add(index); - return collection; - } - - public Phrase getPhrase(int index) { - if (index < 0 || index >= phrases.size()) - return null; - return phrases.get(index); - } + /** + * List of settings. + */ + private final List phrases; + + private final static PhraseManager instance; + + static { + instance = new PhraseManager(); + Application.getInstance().addManager(instance); + } + + public static PhraseManager getInstance() { + return instance; + } + + private PhraseManager() { + phrases = new ArrayList(); + } + + @Override + public void onLoad() { + final Collection phrases = new ArrayList(); + Cursor cursor; + cursor = PhraseTable.getInstance().list(); + try { + if (cursor.moveToFirst()) { + do { + phrases.add(new Phrase(PhraseTable.getId(cursor), + PhraseTable.getValue(cursor), PhraseTable + .getUser(cursor), PhraseTable + .getGroup(cursor), PhraseTable + .isRegexp(cursor), PhraseTable + .getSound(cursor))); + } while (cursor.moveToNext()); + } + } finally { + cursor.close(); + } + Application.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + onLoaded(phrases); + } + }); + } + + private void onLoaded(Collection phrases) { + this.phrases.addAll(phrases); + } + + /** + * @param text + * @return Sound associated with first matched phrase. Chat specific setting + * if no one matches . + */ + public Uri getSound(String account, String user, String text) { + Collection groups = RosterManager.getInstance().getGroups( + account, user); + for (Phrase phrase : phrases) + if (phrase.matches(text, user, groups)) { + Uri value = phrase.getSound(); + if (ChatManager.EMPTY_SOUND.equals(value)) + return null; + return value; + } + return ChatManager.getInstance().getSound(account, user); + } + + /** + * Update phrase or create. + * + * @param phrase can be null for new phrase. + * @param value + * @param regexp + * @param sound + */ + public void updateOrCreatePhrase(Phrase phrase, String value, String user, + String group, boolean regexp, Uri sound) { + if (phrase == null) { + phrase = new Phrase(null, value, user, group, regexp, sound); + phrases.add(phrase); + } else { + phrase.update(value, user, group, regexp, sound); + } + writePhrase(phrase, value, user, group, regexp, sound); + } + + /** + * Removes phrase. + * + * @param index + */ + public void removePhrase(int index) { + phrases.remove(getPhrase(index)); + } + + private void writePhrase(final Phrase phrase, final String value, + final String user, final String group, final boolean regexp, + final Uri sound) { + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + phrase.setId(PhraseTable.getInstance().write(phrase.getId(), + value, user, group, regexp, + sound == null ? ChatManager.EMPTY_SOUND : sound)); + } + }); + } + + public Collection getPhrases() { + Collection collection = new ArrayList(); + for (int index = 0; index < phrases.size(); index++) + collection.add(index); + return collection; + } + + public Phrase getPhrase(int index) { + if (index < 0 || index >= phrases.size()) + return null; + return phrases.get(index); + } } diff --git a/app/src/main/java/com/xabber/android/data/message/phrase/PhraseTable.java b/app/src/main/java/com/xabber/android/data/message/phrase/PhraseTable.java index 401abe7618..fd0b18a8fc 100644 --- a/app/src/main/java/com/xabber/android/data/message/phrase/PhraseTable.java +++ b/app/src/main/java/com/xabber/android/data/message/phrase/PhraseTable.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -25,159 +25,159 @@ /** * Storage with phrase notification settings. - * + * * @author alexander.ivanov */ class PhraseTable extends AbstractTable { - private static final class Fields implements BaseColumns { - - private Fields() { - } - - /** - * Text pattern. - */ - public static final String VALUE = "value"; - - /** - * JID pattern. - */ - public static final String USER = "user"; - - /** - * Contact list group pattern. - */ - public static final String GROUP = "_group"; - - /** - * Whether text should be processed as regexp. - */ - public static final String REGEXP = "regexp"; - - /** - * Used sound. - */ - public static final String SOUND = "sound"; - - } - - private static final String NAME = "phrase"; - private static final String[] PROJECTION = new String[] { Fields._ID, - Fields.VALUE, Fields.USER, Fields.GROUP, Fields.REGEXP, - Fields.SOUND, }; - - private final DatabaseManager databaseManager; - - private final static PhraseTable instance; - - static { - instance = new PhraseTable(DatabaseManager.getInstance()); - DatabaseManager.getInstance().addTable(instance); - } - - public static PhraseTable getInstance() { - return instance; - } - - private PhraseTable(DatabaseManager databaseManager) { - this.databaseManager = databaseManager; - } - - @Override - public void create(SQLiteDatabase db) { - String sql; - sql = "CREATE TABLE " + NAME + " (" + Fields._ID - + " INTEGER PRIMARY KEY," + Fields.VALUE + " TEXT," - + Fields.USER + " TEXT," + Fields.GROUP + " TEXT," - + Fields.REGEXP + " INTEGER," + Fields.SOUND + " TEXT);"; - DatabaseManager.execSQL(db, sql); - } - - @Override - public void migrate(SQLiteDatabase db, int toVersion) { - super.migrate(db, toVersion); - String sql; - switch (toVersion) { - case 63: - sql = "CREATE TABLE phrase (_id INTEGER PRIMARY KEY," - + "value TEXT," + "regexp INTEGER," + "sound TEXT);"; - DatabaseManager.execSQL(db, sql); - break; - case 64: - sql = "ALTER TABLE phrase ADD COLUMN user TEXT;"; - DatabaseManager.execSQL(db, sql); - sql = "UPDATE phrase SET user = \"\";"; - DatabaseManager.execSQL(db, sql); - sql = "ALTER TABLE phrase ADD COLUMN _group TEXT;"; - DatabaseManager.execSQL(db, sql); - sql = "UPDATE phrase SET _group = \"\";"; - DatabaseManager.execSQL(db, sql); - break; - default: - break; - } - } - - long write(Long id, String value, String user, String group, - boolean regexp, Uri sound) { - SQLiteDatabase db = databaseManager.getWritableDatabase(); - ContentValues values = new ContentValues(); - values.put(Fields.VALUE, value); - values.put(Fields.USER, user); - values.put(Fields.GROUP, group); - values.put(Fields.REGEXP, regexp ? 1 : 0); - values.put(Fields.SOUND, sound.toString()); - if (id == null) - return db.insert(NAME, Fields.VALUE, values); - db.update(NAME, values, Fields._ID + " = ?", - new String[] { String.valueOf(id) }); - return id; - } - - void remove(long id) { - SQLiteDatabase db = databaseManager.getWritableDatabase(); - db.delete(NAME, Fields._ID + " = ?", - new String[] { String.valueOf(id) }); - } - - @Override - protected String getTableName() { - return NAME; - } - - @Override - protected String[] getProjection() { - return PROJECTION; - } - - @Override - protected String getListOrder() { - return Fields._ID; - } - - static long getId(Cursor cursor) { - return cursor.getLong(cursor.getColumnIndex(Fields._ID)); - } - - static String getValue(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.VALUE)); - } - - static String getUser(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.USER)); - } - - static String getGroup(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.GROUP)); - } - - static boolean isRegexp(Cursor cursor) { - return cursor.getLong(cursor.getColumnIndex(Fields.REGEXP)) != 0; - } - - static Uri getSound(Cursor cursor) { - return Uri.parse(cursor.getString(cursor.getColumnIndex(Fields.SOUND))); - } + private static final class Fields implements BaseColumns { + + private Fields() { + } + + /** + * Text pattern. + */ + public static final String VALUE = "value"; + + /** + * JID pattern. + */ + public static final String USER = "user"; + + /** + * Contact list group pattern. + */ + public static final String GROUP = "_group"; + + /** + * Whether text should be processed as regexp. + */ + public static final String REGEXP = "regexp"; + + /** + * Used sound. + */ + public static final String SOUND = "sound"; + + } + + private static final String NAME = "phrase"; + private static final String[] PROJECTION = new String[]{Fields._ID, + Fields.VALUE, Fields.USER, Fields.GROUP, Fields.REGEXP, + Fields.SOUND,}; + + private final DatabaseManager databaseManager; + + private final static PhraseTable instance; + + static { + instance = new PhraseTable(DatabaseManager.getInstance()); + DatabaseManager.getInstance().addTable(instance); + } + + public static PhraseTable getInstance() { + return instance; + } + + private PhraseTable(DatabaseManager databaseManager) { + this.databaseManager = databaseManager; + } + + @Override + public void create(SQLiteDatabase db) { + String sql; + sql = "CREATE TABLE " + NAME + " (" + Fields._ID + + " INTEGER PRIMARY KEY," + Fields.VALUE + " TEXT," + + Fields.USER + " TEXT," + Fields.GROUP + " TEXT," + + Fields.REGEXP + " INTEGER," + Fields.SOUND + " TEXT);"; + DatabaseManager.execSQL(db, sql); + } + + @Override + public void migrate(SQLiteDatabase db, int toVersion) { + super.migrate(db, toVersion); + String sql; + switch (toVersion) { + case 63: + sql = "CREATE TABLE phrase (_id INTEGER PRIMARY KEY," + + "value TEXT," + "regexp INTEGER," + "sound TEXT);"; + DatabaseManager.execSQL(db, sql); + break; + case 64: + sql = "ALTER TABLE phrase ADD COLUMN user TEXT;"; + DatabaseManager.execSQL(db, sql); + sql = "UPDATE phrase SET user = \"\";"; + DatabaseManager.execSQL(db, sql); + sql = "ALTER TABLE phrase ADD COLUMN _group TEXT;"; + DatabaseManager.execSQL(db, sql); + sql = "UPDATE phrase SET _group = \"\";"; + DatabaseManager.execSQL(db, sql); + break; + default: + break; + } + } + + long write(Long id, String value, String user, String group, + boolean regexp, Uri sound) { + SQLiteDatabase db = databaseManager.getWritableDatabase(); + ContentValues values = new ContentValues(); + values.put(Fields.VALUE, value); + values.put(Fields.USER, user); + values.put(Fields.GROUP, group); + values.put(Fields.REGEXP, regexp ? 1 : 0); + values.put(Fields.SOUND, sound.toString()); + if (id == null) + return db.insert(NAME, Fields.VALUE, values); + db.update(NAME, values, Fields._ID + " = ?", + new String[]{String.valueOf(id)}); + return id; + } + + void remove(long id) { + SQLiteDatabase db = databaseManager.getWritableDatabase(); + db.delete(NAME, Fields._ID + " = ?", + new String[]{String.valueOf(id)}); + } + + @Override + protected String getTableName() { + return NAME; + } + + @Override + protected String[] getProjection() { + return PROJECTION; + } + + @Override + protected String getListOrder() { + return Fields._ID; + } + + static long getId(Cursor cursor) { + return cursor.getLong(cursor.getColumnIndex(Fields._ID)); + } + + static String getValue(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.VALUE)); + } + + static String getUser(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.USER)); + } + + static String getGroup(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.GROUP)); + } + + static boolean isRegexp(Cursor cursor) { + return cursor.getLong(cursor.getColumnIndex(Fields.REGEXP)) != 0; + } + + static Uri getSound(Cursor cursor) { + return Uri.parse(cursor.getString(cursor.getColumnIndex(Fields.SOUND))); + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/data/notification/AccountNotificationItem.java b/app/src/main/java/com/xabber/android/data/notification/AccountNotificationItem.java index 299dff6bc8..870d2d16e0 100644 --- a/app/src/main/java/com/xabber/android/data/notification/AccountNotificationItem.java +++ b/app/src/main/java/com/xabber/android/data/notification/AccountNotificationItem.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -17,12 +17,11 @@ /** * Account related notification to be displayed. - * + * * @author alexander.ivanov - * */ public interface AccountNotificationItem extends NotificationItem { - public String getAccount(); + String getAccount(); } diff --git a/app/src/main/java/com/xabber/android/data/notification/AccountNotificationProvider.java b/app/src/main/java/com/xabber/android/data/notification/AccountNotificationProvider.java index f41d01a860..e2bca59486 100644 --- a/app/src/main/java/com/xabber/android/data/notification/AccountNotificationProvider.java +++ b/app/src/main/java/com/xabber/android/data/notification/AccountNotificationProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,8 +16,8 @@ public interface AccountNotificationProvider - extends NotificationProvider { + extends NotificationProvider { - void clearAccountNotifications(String account); + void clearAccountNotifications(String account); } diff --git a/app/src/main/java/com/xabber/android/data/notification/BaseAccountNotificationProvider.java b/app/src/main/java/com/xabber/android/data/notification/BaseAccountNotificationProvider.java index 30403edc92..5ae2754f24 100644 --- a/app/src/main/java/com/xabber/android/data/notification/BaseAccountNotificationProvider.java +++ b/app/src/main/java/com/xabber/android/data/notification/BaseAccountNotificationProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,29 +18,29 @@ public class BaseAccountNotificationProvider - extends BaseNotificationProvider implements - AccountNotificationProvider { - - public BaseAccountNotificationProvider(int icon) { - super(icon); - } - - public T get(String account) { - for (T item : items) - if (item.getAccount().equals(account)) - return item; - return null; - } - - public boolean remove(String account) { - return remove(get(account)); - } - - @Override - public void clearAccountNotifications(String account) { - for (Iterator iterator = items.iterator(); iterator.hasNext();) - if (account.equals(iterator.next().getAccount())) - iterator.remove(); - } + extends BaseNotificationProvider implements + AccountNotificationProvider { + + public BaseAccountNotificationProvider(int icon) { + super(icon); + } + + public T get(String account) { + for (T item : items) + if (item.getAccount().equals(account)) + return item; + return null; + } + + public boolean remove(String account) { + return remove(get(account)); + } + + @Override + public void clearAccountNotifications(String account) { + for (Iterator iterator = items.iterator(); iterator.hasNext(); ) + if (account.equals(iterator.next().getAccount())) + iterator.remove(); + } } diff --git a/app/src/main/java/com/xabber/android/data/notification/BaseNotificationProvider.java b/app/src/main/java/com/xabber/android/data/notification/BaseNotificationProvider.java index b94be0641a..cc2a87f9ca 100644 --- a/app/src/main/java/com/xabber/android/data/notification/BaseNotificationProvider.java +++ b/app/src/main/java/com/xabber/android/data/notification/BaseNotificationProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -25,87 +25,85 @@ /** * Base provider for the notifications to be displayed. - * - * @author alexander.ivanov - * + * * @param + * @author alexander.ivanov */ public class BaseNotificationProvider implements - NotificationProvider { - - protected final Collection items; - private final int icon; - private boolean canClearNotifications; - - public BaseNotificationProvider(int icon) { - super(); - this.items = new ArrayList(); - this.icon = icon; - canClearNotifications = true; - } - - /** - * Adds new or update existed notification item and update notification bar. - * - * @param item - * @param notify - * null if notification ticker should be shown only - * if such notification does exists yet. - */ - public void add(T item, Boolean notify) { - boolean exists = items.remove(item); - if (notify == null) - notify = !exists; - items.add(item); - NotificationManager.getInstance().updateNotifications(this, - notify ? item : null); - } - - /** - * Removes notification and update notification bar. - * - * @param item - * @return - */ - public boolean remove(T item) { - boolean result = items.remove(item); - if (result) - NotificationManager.getInstance().updateNotifications(this, null); - return result; - } - - public void setCanClearNotifications(boolean canClearNotifications) { - this.canClearNotifications = canClearNotifications; - } - - @Override - public Collection getNotifications() { - return Collections.unmodifiableCollection(items); - } - - @Override - public boolean canClearNotifications() { - return canClearNotifications; - } - - @Override - public void clearNotifications() { - items.clear(); - } - - @Override - public Uri getSound() { - return SettingsManager.eventsSound(); - } - - @Override - public int getStreamType() { - return AudioManager.STREAM_NOTIFICATION; - } - - @Override - public int getIcon() { - return icon; - } + NotificationProvider { + + protected final Collection items; + private final int icon; + private boolean canClearNotifications; + + public BaseNotificationProvider(int icon) { + super(); + this.items = new ArrayList(); + this.icon = icon; + canClearNotifications = true; + } + + /** + * Adds new or update existed notification item and update notification bar. + * + * @param item + * @param notify null if notification ticker should be shown only + * if such notification does exists yet. + */ + public void add(T item, Boolean notify) { + boolean exists = items.remove(item); + if (notify == null) + notify = !exists; + items.add(item); + NotificationManager.getInstance().updateNotifications(this, + notify ? item : null); + } + + /** + * Removes notification and update notification bar. + * + * @param item + * @return + */ + public boolean remove(T item) { + boolean result = items.remove(item); + if (result) + NotificationManager.getInstance().updateNotifications(this, null); + return result; + } + + public void setCanClearNotifications(boolean canClearNotifications) { + this.canClearNotifications = canClearNotifications; + } + + @Override + public Collection getNotifications() { + return Collections.unmodifiableCollection(items); + } + + @Override + public boolean canClearNotifications() { + return canClearNotifications; + } + + @Override + public void clearNotifications() { + items.clear(); + } + + @Override + public Uri getSound() { + return SettingsManager.eventsSound(); + } + + @Override + public int getStreamType() { + return AudioManager.STREAM_NOTIFICATION; + } + + @Override + public int getIcon() { + return icon; + } } diff --git a/app/src/main/java/com/xabber/android/data/notification/EntityNotificationItem.java b/app/src/main/java/com/xabber/android/data/notification/EntityNotificationItem.java index f3c76dbbf1..e8f4d8b690 100644 --- a/app/src/main/java/com/xabber/android/data/notification/EntityNotificationItem.java +++ b/app/src/main/java/com/xabber/android/data/notification/EntityNotificationItem.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,12 +16,11 @@ /** * Entity related notification to be displayed. - * + * * @author alexander.ivanov - * */ public interface EntityNotificationItem extends AccountNotificationItem { - public String getUser(); + String getUser(); } diff --git a/app/src/main/java/com/xabber/android/data/notification/EntityNotificationProvider.java b/app/src/main/java/com/xabber/android/data/notification/EntityNotificationProvider.java index de772cefb0..1e54daf23b 100644 --- a/app/src/main/java/com/xabber/android/data/notification/EntityNotificationProvider.java +++ b/app/src/main/java/com/xabber/android/data/notification/EntityNotificationProvider.java @@ -1,41 +1,41 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data.notification; public class EntityNotificationProvider - extends BaseAccountNotificationProvider { + extends BaseAccountNotificationProvider { - public EntityNotificationProvider(int icon) { - super(icon); - } + public EntityNotificationProvider(int icon) { + super(icon); + } - @Override - public T get(String account) { - throw new UnsupportedOperationException(); - } + @Override + public T get(String account) { + throw new UnsupportedOperationException(); + } - public T get(String account, String user) { - for (T item : items) - if (item.getAccount().equals(account) - && item.getUser().equals(user)) - return item; - return null; - } + public T get(String account, String user) { + for (T item : items) + if (item.getAccount().equals(account) + && item.getUser().equals(user)) + return item; + return null; + } - public boolean remove(String account, String user) { - return remove(get(account, user)); - } + public boolean remove(String account, String user) { + return remove(get(account, user)); + } } diff --git a/app/src/main/java/com/xabber/android/data/notification/MessageNotification.java b/app/src/main/java/com/xabber/android/data/notification/MessageNotification.java index b35314bee7..fda9b1447b 100644 --- a/app/src/main/java/com/xabber/android/data/notification/MessageNotification.java +++ b/app/src/main/java/com/xabber/android/data/notification/MessageNotification.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,51 +20,50 @@ /** * Notification for the contact. - * + * * @author alexander.ivanov - * */ public class MessageNotification extends BaseEntity { - /** - * Text of the last message. - */ - private String text; + /** + * Text of the last message. + */ + private String text; - /** - * Timestamp of the last message. - */ - private Date timestamp; + /** + * Timestamp of the last message. + */ + private Date timestamp; - /** - * Number of messages. - */ - private int count; + /** + * Number of messages. + */ + private int count; - public MessageNotification(String account, String user, String text, - Date timestamp, int count) { - super(account, user); - this.text = text; - this.timestamp = timestamp; - this.count = count; - } + public MessageNotification(String account, String user, String text, + Date timestamp, int count) { + super(account, user); + this.text = text; + this.timestamp = timestamp; + this.count = count; + } - public String getText() { - return text; - } + public String getText() { + return text; + } - public Date getTimestamp() { - return timestamp; - } + public Date getTimestamp() { + return timestamp; + } - public int getCount() { - return count; - } + public int getCount() { + return count; + } - public void addMessage(String text) { - this.text = text; - this.timestamp = new Date(); - this.count += 1; - } + public void addMessage(String text) { + this.text = text; + this.timestamp = new Date(); + this.count += 1; + } } diff --git a/app/src/main/java/com/xabber/android/data/notification/MessageNotificationCreator.java b/app/src/main/java/com/xabber/android/data/notification/MessageNotificationCreator.java new file mode 100644 index 0000000000..21f9c08cdc --- /dev/null +++ b/app/src/main/java/com/xabber/android/data/notification/MessageNotificationCreator.java @@ -0,0 +1,207 @@ +package com.xabber.android.data.notification; + +import android.app.PendingIntent; +import android.content.Intent; +import android.support.v4.app.NotificationCompat; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.style.StyleSpan; + +import com.xabber.android.R; +import com.xabber.android.data.Application; +import com.xabber.android.data.extension.avatar.AvatarManager; +import com.xabber.android.data.extension.muc.MUCManager; +import com.xabber.android.data.message.MessageItem; +import com.xabber.android.data.message.chat.ChatManager; +import com.xabber.android.data.roster.RosterManager; +import com.xabber.android.ui.ChatViewer; +import com.xabber.android.ui.ContactList; +import com.xabber.android.ui.helper.AccountPainter; +import com.xabber.android.utils.StringUtils; + +import java.util.List; + +public class MessageNotificationCreator { + + private static int UNIQUE_REQUEST_CODE = 0; + private final Application application; + private final AccountPainter accountPainter; + private List messageNotifications; + + public MessageNotificationCreator() { + application = Application.getInstance(); + accountPainter = new AccountPainter(application); + + } + + public android.app.Notification notifyMessageNotification(List messageNotifications, + MessageItem messageItem) { + this.messageNotifications = messageNotifications; + + if (messageNotifications.isEmpty()) { + return null; + } + + int messageCount = 0; + + for (MessageNotification messageNotification : messageNotifications) { + messageCount += messageNotification.getCount(); + } + + + MessageNotification message = messageNotifications.get(messageNotifications.size() - 1); + + boolean showText = ChatManager.getInstance().isShowText(message.getAccount(), message.getUser()); + + NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(application); + notificationBuilder.setContentTitle(getTitle(message, messageCount)); + notificationBuilder.setContentText(getText(message, showText)); + notificationBuilder.setSubText(message.getAccount()); + + notificationBuilder.setTicker(getText(message, showText)); + + notificationBuilder.setSmallIcon(getSmallIcon()); + notificationBuilder.setLargeIcon(getLargeIcon(message)); + + notificationBuilder.setWhen(message.getTimestamp().getTime()); + notificationBuilder.setColor(accountPainter.getAccountMainColor(message.getAccount())); + notificationBuilder.setStyle(getStyle(message, messageCount, showText)); + + notificationBuilder.setContentIntent(getIntent(message)); + + notificationBuilder.setCategory(NotificationCompat.CATEGORY_MESSAGE); + notificationBuilder.setPriority(NotificationCompat.PRIORITY_HIGH); + + NotificationManager.addEffects(notificationBuilder, messageItem); + + return notificationBuilder.build(); + } + + private CharSequence getTitle(MessageNotification message, int messageCount) { + if (isFromOneContact()) { + return getSingleContactTitle(message, messageCount); + } else { + return getMultiContactTitle(messageCount); + } + } + + private CharSequence getSingleContactTitle(MessageNotification message, int messageCount) { + if (messageCount > 1) { + return application.getString(R.string.chat_messages_from_contact, + messageCount, getTextForMessages(messageCount), getContactName(message)); + } else { + return getContactName(message); + } + } + + private String getContactName(MessageNotification message) { + return RosterManager.getInstance().getName(message.getAccount(), message.getUser()); + } + + private CharSequence getMultiContactTitle(int messageCount) { + String messageText = getTextForMessages(messageCount); + String contactText = StringUtils.getQuantityString(application.getResources(), + R.array.chat_contact_quantity, messageNotifications.size()); + return application.getString(R.string.chat_status, + messageCount, messageText, messageNotifications.size(), contactText); + } + + private String getTextForMessages(int messageCount) { + return StringUtils.getQuantityString( + application.getResources(), R.array.chat_message_quantity, messageCount); + } + + private CharSequence getText(MessageNotification message, boolean showText) { + if (isFromOneContact()) { + if (showText) { + return message.getText(); + } else { + return null; + } + } else { + return getContactNameAndMessage(message, showText); + } + } + + private int getSmallIcon() { + return R.drawable.ic_stat_chat; + } + + private android.graphics.Bitmap getLargeIcon(MessageNotification message) { + if (isFromOneContact()) { + if (MUCManager.getInstance().hasRoom(message.getAccount(), message.getUser())) { + return AvatarManager.getInstance().getRoomBitmap(message.getUser()); + } else { + return AvatarManager.getInstance().getUserBitmap(message.getUser()); + } + } + return null; + } + + private boolean isFromOneContact() { + return messageNotifications.size() == 1; + } + + private NotificationCompat.Style getStyle(MessageNotification message, int messageCount, boolean showText) { + if (isFromOneContact()) { + NotificationCompat.BigTextStyle bigTextStyle = new NotificationCompat.BigTextStyle(); + + bigTextStyle.setBigContentTitle(getSingleContactTitle(message, messageCount)); + if (showText) { + bigTextStyle.bigText(message.getText()); + } + bigTextStyle.setSummaryText(message.getAccount()); + + return bigTextStyle; + } else { + return getInboxStyle(messageCount, message.getAccount()); + } + } + + private NotificationCompat.Style getInboxStyle(int messageCount, String accountName) { + NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle(); + + inboxStyle.setBigContentTitle(getMultiContactTitle(messageCount)); + + for (int i = 1; i <= messageNotifications.size(); i++) { + MessageNotification messageNotification = messageNotifications.get(messageNotifications.size() - i); + + boolean showTextForThisContact + = ChatManager.getInstance().isShowText(messageNotification.getAccount(), messageNotification.getUser()); + + inboxStyle.addLine(getContactNameAndMessage(messageNotification, showTextForThisContact)); + } + + inboxStyle.setSummaryText(accountName); + + return inboxStyle; + } + + private Spannable getContactNameAndMessage(MessageNotification messageNotification, boolean showText) { + String userName = getContactName(messageNotification); + + Spannable spannableString; + if (showText) { + String contactAndMessage = application.getString( + R.string.chat_contact_and_message, userName, messageNotification.getText()); + spannableString = new SpannableString(contactAndMessage); + + } else { + spannableString = new SpannableString(userName); + } + + spannableString.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 0, userName.length(), + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + return spannableString; + } + + private PendingIntent getIntent(MessageNotification message) { + Intent backIntent = ContactList.createIntent(application); + backIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + Intent intent = ChatViewer.createClearTopIntent(application, message.getAccount(), message.getUser()); + return PendingIntent.getActivities(application, UNIQUE_REQUEST_CODE++, + new Intent[]{backIntent, intent}, PendingIntent.FLAG_ONE_SHOT); + } + +} diff --git a/app/src/main/java/com/xabber/android/data/notification/NotificationItem.java b/app/src/main/java/com/xabber/android/data/notification/NotificationItem.java index 9d1aaa9f19..b735f9929b 100644 --- a/app/src/main/java/com/xabber/android/data/notification/NotificationItem.java +++ b/app/src/main/java/com/xabber/android/data/notification/NotificationItem.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,25 +18,24 @@ /** * Notification to be displayed. - * + * * @author alexander.ivanov - * */ public interface NotificationItem { - /** - * @return Intent to launch activity. - */ - Intent getIntent(); + /** + * @return Intent to launch activity. + */ + Intent getIntent(); - /** - * @return Title for notification bar. - */ - String getTitle(); + /** + * @return Title for notification bar. + */ + String getTitle(); - /** - * @return Text for notification bar. - */ - String getText(); + /** + * @return Text for notification bar. + */ + String getText(); } diff --git a/app/src/main/java/com/xabber/android/data/notification/NotificationManager.java b/app/src/main/java/com/xabber/android/data/notification/NotificationManager.java index 7c79c1e76d..36a268fb34 100644 --- a/app/src/main/java/com/xabber/android/data/notification/NotificationManager.java +++ b/app/src/main/java/com/xabber/android/data/notification/NotificationManager.java @@ -1,25 +1,19 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data.notification; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.Iterator; -import java.util.List; - import android.app.Notification; import android.app.PendingIntent; import android.content.Context; @@ -29,8 +23,9 @@ import android.net.Uri; import android.os.Handler; import android.os.Vibrator; -import android.widget.RemoteViews; +import android.support.v4.app.NotificationCompat; +import com.xabber.android.R; import com.xabber.android.data.Application; import com.xabber.android.data.LogManager; import com.xabber.android.data.OnCloseListener; @@ -44,610 +39,552 @@ import com.xabber.android.data.account.OnAccountChangedListener; import com.xabber.android.data.account.OnAccountRemovedListener; import com.xabber.android.data.connection.ConnectionState; -import com.xabber.android.data.extension.avatar.AvatarManager; -import com.xabber.android.data.extension.muc.MUCManager; import com.xabber.android.data.message.MessageItem; import com.xabber.android.data.message.MessageManager; import com.xabber.android.data.message.chat.ChatManager; import com.xabber.android.data.message.phrase.PhraseManager; import com.xabber.android.data.roster.RosterManager; -import com.xabber.android.ui.ChatViewer; import com.xabber.android.ui.ClearNotifications; import com.xabber.android.ui.ContactList; import com.xabber.android.ui.ReconnectionActivity; -import com.xabber.android.utils.Emoticons; +import com.xabber.android.ui.helper.AccountPainter; import com.xabber.android.utils.StringUtils; -import com.xabber.androiddev.R; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.Iterator; +import java.util.List; /** * Manage notifications about message, subscription and authentication. - * + * * @author alexander.ivanov */ -public class NotificationManager implements OnInitializedListener, - OnAccountChangedListener, OnCloseListener, OnLoadListener, Runnable, - OnAccountRemovedListener, OnAccountArchiveModeChangedListener { - - public static final int PERSISTENT_NOTIFICATION_ID = 1; - private static final int CHAT_NOTIFICATION_ID = 2; - private static final int BASE_NOTIFICATION_PROVIDER_ID = 0x10; - - private static final long VIBRATION_DURATION = 500; - private static final int MAX_NOTIFICATION_TEXT = 80; - private final long startTime; - private final Application application; - private final android.app.NotificationManager notificationManager; - private final Notification persistentNotification; - private final PendingIntent clearNotifications; - private final Handler handler; - - /** - * Runnable to start vibration. - */ - private final Runnable startVibro; - - /** - * Runnable to force stop vibration. - */ - private final Runnable stopVibro; - - /** - * List of providers for notifications. - */ - private final List> providers; - - /** - * List of - */ - private final List messageNotifications; - - private final static NotificationManager instance; - - static { - instance = new NotificationManager(); - Application.getInstance().addManager(instance); - } - - public static NotificationManager getInstance() { - return instance; - } - - private NotificationManager() { - this.application = Application.getInstance(); - notificationManager = (android.app.NotificationManager) application - .getSystemService(Context.NOTIFICATION_SERVICE); - persistentNotification = new Notification(); - handler = new Handler(); - providers = new ArrayList>(); - messageNotifications = new ArrayList(); - startTime = System.currentTimeMillis(); - clearNotifications = PendingIntent.getActivity(application, 0, - ClearNotifications.createIntent(application), 0); - stopVibro = new Runnable() { - @Override - public void run() { - handler.removeCallbacks(startVibro); - handler.removeCallbacks(stopVibro); - ((Vibrator) NotificationManager.this.application - .getSystemService(Context.VIBRATOR_SERVICE)).cancel(); - } - }; - startVibro = new Runnable() { - @Override - public void run() { - handler.removeCallbacks(startVibro); - handler.removeCallbacks(stopVibro); - ((Vibrator) NotificationManager.this.application - .getSystemService(Context.VIBRATOR_SERVICE)).cancel(); - ((Vibrator) NotificationManager.this.application - .getSystemService(Context.VIBRATOR_SERVICE)) - .vibrate(VIBRATION_DURATION); - handler.postDelayed(stopVibro, VIBRATION_DURATION); - } - }; - } - - @Override - public void onLoad() { - final Collection messageNotifications = new ArrayList(); - Cursor cursor = NotificationTable.getInstance().list(); - try { - if (cursor.moveToFirst()) { - do { - messageNotifications.add(new MessageNotification( - NotificationTable.getAccount(cursor), - NotificationTable.getUser(cursor), - NotificationTable.getText(cursor), - NotificationTable.getTimeStamp(cursor), - NotificationTable.getCount(cursor))); - } while (cursor.moveToNext()); - } - } finally { - cursor.close(); - } - Application.getInstance().runOnUiThread(new Runnable() { - @Override - public void run() { - onLoaded(messageNotifications); - } - }); - } - - private void onLoaded(Collection messageNotifications) { - this.messageNotifications.addAll(messageNotifications); - for (MessageNotification messageNotification : messageNotifications) - MessageManager.getInstance().openChat( - messageNotification.getAccount(), - messageNotification.getUser()); - } - - @Override - public void onInitialized() { - application.addUIListener(OnAccountChangedListener.class, this); - updateMessageNotification(null); - } - - /** - * Register new provider for notifications. - * - * @param provider - */ - public void registerNotificationProvider( - NotificationProvider provider) { - providers.add(provider); - } - - /** - * Update notifications for specified provider. - * - * @param - * @param provider - * @param notify - * Ticker to be shown. Can be null. - */ - public void updateNotifications( - NotificationProvider provider, T notify) { - int id = providers.indexOf(provider); - if (id == -1) - throw new IllegalStateException( - "registerNotificationProvider() must be called from onLoaded() method."); - else - id += BASE_NOTIFICATION_PROVIDER_ID; - Iterator iterator = provider - .getNotifications().iterator(); - if (!iterator.hasNext()) { - notificationManager.cancel(id); - } else { - NotificationItem top; - String ticker; - if (notify == null) { - top = iterator.next(); - ticker = null; - } else { - top = notify; - ticker = top.getTitle(); - } - Intent intent = top.getIntent(); - Notification notification = new Notification(provider.getIcon(), - ticker, System.currentTimeMillis()); - if (!provider.canClearNotifications()) - notification.flags |= Notification.FLAG_NO_CLEAR; - notification.setLatestEventInfo(application, top.getTitle(), top - .getText(), PendingIntent.getActivity(application, 0, - intent, PendingIntent.FLAG_UPDATE_CURRENT)); - if (ticker != null) - setNotificationDefaults(notification, - SettingsManager.eventsVibro(), provider.getSound(), - provider.getStreamType()); - notification.deleteIntent = clearNotifications; - notify(id, notification); - } - } - - /** - * Sound, vibration and lightning flags. - * - * @param notification - * @param streamType - */ - private void setNotificationDefaults(Notification notification, - boolean vibro, Uri sound, int streamType) { - notification.audioStreamType = streamType; - notification.defaults = 0; - notification.sound = sound; - if (vibro) { - if (SettingsManager.eventsIgnoreSystemVibro()) - handler.post(startVibro); - else - notification.defaults |= Notification.DEFAULT_VIBRATE; - } - if (SettingsManager.eventsLightning()) { - notification.defaults |= Notification.DEFAULT_LIGHTS; - notification.flags |= Notification.FLAG_SHOW_LIGHTS; - } - } - - /** - * Chat was changed: - *
    - *
  • incoming message
  • - *
  • chat was opened
  • - *
  • account was changed
  • - *
- * - * Update chat and persistent notifications. - * - * @param ticker - * message to be shown. - * @return - */ - private void updateMessageNotification(MessageItem ticker) { - Collection accountList = AccountManager.getInstance() - .getAccounts(); - int accountCount = accountList.size(); - boolean started = application.isInitialized(); - int waiting = 0; - int connecting = 0; - int connected = 0; - for (String account : accountList) { - ConnectionState state = AccountManager.getInstance() - .getAccount(account).getState(); - if (RosterManager.getInstance().isRosterReceived(account)) - connected++; - else if (state == ConnectionState.connecting - || state == ConnectionState.authentication) - connecting++; - else if (state == ConnectionState.waiting) - waiting++; - } - - String accountQuantity; - String connectionState; - if (connected > 0) { - accountQuantity = StringUtils.getQuantityString( - application.getResources(), R.array.account_quantity, - accountCount); - String connectionFormat = StringUtils.getQuantityString( - application.getResources(), - R.array.connection_state_connected, connected); - connectionState = String.format(connectionFormat, connected, - accountCount, accountQuantity); - } else if (connecting > 0) { - accountQuantity = StringUtils.getQuantityString( - application.getResources(), R.array.account_quantity, - accountCount); - String connectionFormat = StringUtils.getQuantityString( - application.getResources(), - R.array.connection_state_connecting, connecting); - connectionState = String.format(connectionFormat, connecting, - accountCount, accountQuantity); - } else if (waiting > 0 && started) { - accountQuantity = StringUtils.getQuantityString( - application.getResources(), R.array.account_quantity, - accountCount); - String connectionFormat = StringUtils.getQuantityString( - application.getResources(), - R.array.connection_state_waiting, waiting); - connectionState = String.format(connectionFormat, waiting, - accountCount, accountQuantity); - } else { - accountQuantity = StringUtils.getQuantityString( - application.getResources(), - R.array.account_quantity_offline, accountCount); - connectionState = application.getString( - R.string.connection_state_offline, accountCount, - accountQuantity); - } - - final Intent persistentIntent; - if (waiting > 0 && started) - persistentIntent = ReconnectionActivity.createIntent(application); - else - persistentIntent = ContactList.createPersistentIntent(application); - - if (messageNotifications.isEmpty()) { - notificationManager.cancel(CHAT_NOTIFICATION_ID); - } else { - int messageCount = 0; - for (MessageNotification messageNotification : messageNotifications) - messageCount += messageNotification.getCount(); - MessageNotification message = messageNotifications - .get(messageNotifications.size() - 1); - - RemoteViews chatViews = new RemoteViews( - application.getPackageName(), R.layout.chat_notification); - - Intent chatIntent = ChatViewer.createClearTopIntent(application, - message.getAccount(), message.getUser()); - if (MUCManager.getInstance().hasRoom(message.getAccount(), - message.getUser())) - chatViews.setImageViewBitmap(R.id.icon, AvatarManager - .getInstance().getRoomBitmap(message.getUser())); - else - chatViews.setImageViewBitmap(R.id.icon, AvatarManager - .getInstance().getUserBitmap(message.getUser())); - chatViews.setTextViewText(R.id.title, RosterManager.getInstance() - .getName(message.getAccount(), message.getUser())); - String text; - if (ChatManager.getInstance().isShowText(message.getAccount(), - message.getUser())) - text = trimText(message.getText()); - else - text = ""; - chatViews.setTextViewText(R.id.text2, - Emoticons.getSmiledText(application, text)); - chatViews.setTextViewText(R.id.time, - StringUtils.getSmartTimeText(message.getTimestamp())); - - String messageText = StringUtils.getQuantityString( - application.getResources(), R.array.chat_message_quantity, - messageCount); - String contactText = StringUtils.getQuantityString( - application.getResources(), R.array.chat_contact_quantity, - messageNotifications.size()); - String status = application.getString(R.string.chat_status, - messageCount, messageText, messageNotifications.size(), - contactText); - chatViews.setTextViewText(R.id.text, status); - - Notification notification = new Notification(); - if (Application.SDK_INT >= 14 && SettingsManager.eventsPersistent()) { - // Ongoing icons are in the left side, so hide this one. - notification.icon = R.drawable.ic_placeholder; - notification.when = Long.MIN_VALUE; - } else { - // Ongoing icons are in the right side, so show this one. - updateNotification(notification, ticker); - notification.icon = connected > 0 ? R.drawable.ic_stat_message - : R.drawable.ic_stat_message_offline; - notification.when = System.currentTimeMillis(); - } - notification.contentView = chatViews; - notification.contentIntent = PendingIntent.getActivity(application, - 0, chatIntent, PendingIntent.FLAG_UPDATE_CURRENT); - notification.deleteIntent = clearNotifications; - - try { - notify(CHAT_NOTIFICATION_ID, notification); - } catch (RuntimeException e) { - LogManager.exception(this, e); - // Try to remove avatar in order to avoid - // OutOfMemoryError on the Android system side. - chatViews.setImageViewResource(R.id.icon, - R.drawable.ic_placeholder); - notify(CHAT_NOTIFICATION_ID, notification); - } - } - - persistentNotification.icon = R.drawable.ic_stat_normal; - persistentNotification.setLatestEventInfo(application, application - .getString(R.string.application_name), connectionState, - PendingIntent.getActivity(application, 0, persistentIntent, - PendingIntent.FLAG_UPDATE_CURRENT)); - persistentNotification.flags = Notification.FLAG_ONGOING_EVENT - | Notification.FLAG_NO_CLEAR; - persistentNotification.defaults = 0; - persistentNotification.sound = null; - persistentNotification.tickerText = null; - if (Application.SDK_INT >= 14 && SettingsManager.eventsPersistent()) { - // Ongoing icons are in the left side, so always use it. - persistentNotification.when = startTime; - if (messageNotifications.isEmpty()) { - persistentNotification.icon = connected > 0 ? R.drawable.ic_stat_normal - : R.drawable.ic_stat_offline; - } else { - persistentNotification.icon = connected > 0 ? R.drawable.ic_stat_message - : R.drawable.ic_stat_message_offline; - } - updateNotification(persistentNotification, ticker); - } else { - // Ongoing icons are in the right side, so hide it if necessary. - if (messageNotifications.isEmpty()) { - persistentNotification.icon = connected > 0 ? R.drawable.ic_stat_normal - : R.drawable.ic_stat_offline; - persistentNotification.when = startTime; - // Show ticker for the messages in active chat. - updateNotification(persistentNotification, ticker); - } else { - persistentNotification.icon = R.drawable.ic_placeholder; - persistentNotification.when = Application.SDK_INT >= 9 ? -Long.MAX_VALUE - : Long.MAX_VALUE; - } - } - - if (SettingsManager.eventsPersistent()) { - notify(PERSISTENT_NOTIFICATION_ID, persistentNotification); - } else { - notificationManager.cancel(PERSISTENT_NOTIFICATION_ID); - } - } - - private void notify(int id, Notification notification) { - LogManager.i(this, "Notification: " + id + ", ticker: " - + notification.tickerText + ", sound: " + notification.sound - + ", vibro: " - + (notification.defaults & Notification.DEFAULT_VIBRATE) - + ", light: " - + (notification.defaults & Notification.DEFAULT_LIGHTS)); - try { - notificationManager.notify(id, notification); - } catch (SecurityException e) { - } - } - - /** - * Update notification according to ticker. - * - * @param notification - * @param ticker - */ - private void updateNotification(Notification notification, - MessageItem ticker) { - if (ticker == null) - return; - if (ticker.getChat().getFirstNotification() - || !SettingsManager.eventsFirstOnly()) - setNotificationDefaults( - notification, - ChatManager.getInstance().isMakeVibro( - ticker.getChat().getAccount(), - ticker.getChat().getUser()), - PhraseManager.getInstance().getSound( - ticker.getChat().getAccount(), - ticker.getChat().getUser(), ticker.getText()), - AudioManager.STREAM_NOTIFICATION); - if (ChatManager.getInstance().isShowText(ticker.getChat().getAccount(), - ticker.getChat().getUser())) - notification.tickerText = trimText(ticker.getText()); - } - - private MessageNotification getMessageNotification(String account, - String user) { - for (MessageNotification messageNotification : messageNotifications) - if (messageNotification.equals(account, user)) - return messageNotification; - return null; - } - - /** - * Shows ticker with the message and updates message notification. - * - * @param messageItem - * @param addNotification - * Whether notification should be stored. - */ - public void onMessageNotification(MessageItem messageItem, - boolean addNotification) { - if (addNotification) { - MessageNotification messageNotification = getMessageNotification( - messageItem.getChat().getAccount(), messageItem.getChat() - .getUser()); - if (messageNotification == null) - messageNotification = new MessageNotification(messageItem - .getChat().getAccount(), messageItem.getChat() - .getUser(), null, null, 0); - else - messageNotifications.remove(messageNotification); - messageNotification.addMessage(messageItem.getText()); - messageNotifications.add(messageNotification); - final String account = messageNotification.getAccount(); - final String user = messageNotification.getUser(); - final String text = messageNotification.getText(); - final Date timestamp = messageNotification.getTimestamp(); - final int count = messageNotification.getCount(); - if (AccountManager.getInstance().getArchiveMode(account) != ArchiveMode.dontStore) - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - NotificationTable.getInstance().write(account, user, - text, timestamp, count); - } - }); - } - updateMessageNotification(messageItem); - } - - /** - * Updates message notification. - */ - public void onMessageNotification() { - updateMessageNotification(null); - } - - public int getNotificationMessageCount(String account, String user) { - MessageNotification messageNotification = getMessageNotification( - account, user); - if (messageNotification == null) - return 0; - return messageNotification.getCount(); - } - - public void removeMessageNotification(final String account, - final String user) { - MessageNotification messageNotification = getMessageNotification( - account, user); - if (messageNotification == null) - return; - messageNotifications.remove(messageNotification); - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - NotificationTable.getInstance().remove(account, user); - } - }); - updateMessageNotification(null); - } - - /** - * Called when notifications was cleared by user. - */ - public void onClearNotifications() { - for (NotificationProvider provider : providers) - if (provider.canClearNotifications()) - provider.clearNotifications(); - messageNotifications.clear(); - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - NotificationTable.getInstance().clear(); - } - }); - updateMessageNotification(null); - } - - @Override - public void onAccountArchiveModeChanged(AccountItem accountItem) { - final String account = accountItem.getAccount(); - if (AccountManager.getInstance().getArchiveMode(account) != ArchiveMode.dontStore) - return; - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - NotificationTable.getInstance().removeAccount(account); - } - }); - } - - @Override - public void onAccountsChanged(Collection accounts) { - handler.post(this); - } - - @Override - public void onAccountRemoved(AccountItem accountItem) { - for (NotificationProvider notificationProvider : providers) - if (notificationProvider instanceof AccountNotificationProvider) { - ((AccountNotificationProvider) notificationProvider) - .clearAccountNotifications(accountItem.getAccount()); - updateNotifications(notificationProvider, null); - } - } - - @Override - public void run() { - handler.removeCallbacks(this); - updateMessageNotification(null); - } - - public Notification getPersistentNotification() { - return persistentNotification; - } - - @Override - public void onClose() { - notificationManager.cancelAll(); - } - - /** - * @param text - * @return Trimmed text. - */ - private static String trimText(String text) { - if (text.length() > MAX_NOTIFICATION_TEXT) - return text.substring(0, MAX_NOTIFICATION_TEXT - 3) + "..."; - else - return text; - - } - +public class NotificationManager implements OnInitializedListener, OnAccountChangedListener, + OnCloseListener, OnLoadListener, Runnable, OnAccountRemovedListener, + OnAccountArchiveModeChangedListener { + + public static final int PERSISTENT_NOTIFICATION_ID = 1; + public static final int MESSAGE_NOTIFICATION_ID = 2; + public static final int CURRENT_CHAT_MESSAGE_NOTIFICATION_ID = 3; + private static final int BASE_NOTIFICATION_PROVIDER_ID = 0x10; + + private static final long VIBRATION_DURATION = 500; + private final static NotificationManager instance; + + static { + instance = new NotificationManager(); + Application.getInstance().addManager(instance); + } + + private final Application application; + private final android.app.NotificationManager notificationManager; + private final PendingIntent clearNotifications; + private final Handler handler; + /** + * Runnable to start vibration. + */ + private final Runnable startVibration; + + /** + * Runnable to force stop vibration. + */ + private final Runnable stopVibration; + + /** + * List of providers for notifications. + */ + private final List> providers; + + /** + * List of + */ + private final List messageNotifications; + private final AccountPainter accountPainter; + private NotificationCompat.Builder persistentNotificationBuilder; + private MessageNotificationCreator messageNotificationCreator; + private int persistentNotificationColor; + + private NotificationManager() { + this.application = Application.getInstance(); + + notificationManager = (android.app.NotificationManager) + application.getSystemService(Context.NOTIFICATION_SERVICE); + + + handler = new Handler(); + providers = new ArrayList<>(); + messageNotifications = new ArrayList<>(); + clearNotifications = PendingIntent.getActivity( + application, 0, ClearNotifications.createIntent(application), 0); + + stopVibration = new Runnable() { + @Override + public void run() { + handler.removeCallbacks(startVibration); + handler.removeCallbacks(stopVibration); + ((Vibrator) NotificationManager.this.application. + getSystemService(Context.VIBRATOR_SERVICE)).cancel(); + } + }; + + startVibration = new Runnable() { + @Override + public void run() { + handler.removeCallbacks(startVibration); + handler.removeCallbacks(stopVibration); + ((Vibrator) NotificationManager.this.application + .getSystemService(Context.VIBRATOR_SERVICE)).cancel(); + ((Vibrator) NotificationManager.this.application + .getSystemService(Context.VIBRATOR_SERVICE)) + .vibrate(VIBRATION_DURATION); + handler.postDelayed(stopVibration, VIBRATION_DURATION); + } + }; + + persistentNotificationBuilder = new NotificationCompat.Builder(application); + initPersistentNotification(); + + + messageNotificationCreator = new MessageNotificationCreator(); + + accountPainter = new AccountPainter(application); + persistentNotificationColor = application.getResources().getColor(R.color.persistent_notification_color); + } + + public static NotificationManager getInstance() { + return instance; + } + + public static void addEffects(NotificationCompat.Builder notificationBuilder, MessageItem messageItem) { + if (messageItem == null) { + return; + } + if (messageItem.getChat().getFirstNotification() || !SettingsManager.eventsFirstOnly()) { + Uri sound = PhraseManager.getInstance().getSound(messageItem.getChat().getAccount(), + messageItem.getChat().getUser(), messageItem.getText()); + boolean makeVibration = ChatManager.getInstance().isMakeVibro(messageItem.getChat().getAccount(), + messageItem.getChat().getUser()); + + NotificationManager.getInstance().setNotificationDefaults(notificationBuilder, + makeVibration, sound, AudioManager.STREAM_NOTIFICATION); + } + } + + private void initPersistentNotification() { + persistentNotificationBuilder.setContentTitle(application.getString(R.string.application_title_full)); + persistentNotificationBuilder.setDeleteIntent(clearNotifications); + persistentNotificationBuilder.setOngoing(true); + persistentNotificationBuilder.setWhen(System.currentTimeMillis()); + persistentNotificationBuilder.setCategory(NotificationCompat.CATEGORY_SERVICE); + persistentNotificationBuilder.setPriority(NotificationCompat.PRIORITY_LOW); + persistentNotificationBuilder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC); + + } + + @Override + public void onLoad() { + final Collection messageNotifications = new ArrayList<>(); + Cursor cursor = NotificationTable.getInstance().list(); + try { + if (cursor.moveToFirst()) { + do { + messageNotifications.add(new MessageNotification( + NotificationTable.getAccount(cursor), + NotificationTable.getUser(cursor), + NotificationTable.getText(cursor), + NotificationTable.getTimeStamp(cursor), + NotificationTable.getCount(cursor))); + } while (cursor.moveToNext()); + } + } finally { + cursor.close(); + } + Application.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + onLoaded(messageNotifications); + } + }); + } + + private void onLoaded(Collection messageNotifications) { + this.messageNotifications.addAll(messageNotifications); + for (MessageNotification messageNotification : messageNotifications) { + MessageManager.getInstance().openChat( + messageNotification.getAccount(), + messageNotification.getUser()); + } + } + + @Override + public void onInitialized() { + application.addUIListener(OnAccountChangedListener.class, this); + updateMessageNotification(null); + } + + /** + * Register new provider for notifications. + * + * @param provider + */ + public void registerNotificationProvider(NotificationProvider provider) { + providers.add(provider); + } + + /** + * Update notifications for specified provider. + * + * @param + * @param provider + * @param notify Ticker to be shown. Can be null. + */ + public void updateNotifications( + NotificationProvider provider, T notify) { + int id = providers.indexOf(provider); + + if (id == -1) { + throw new IllegalStateException( + "registerNotificationProvider() must be called from onLoaded() method."); + } + + id += BASE_NOTIFICATION_PROVIDER_ID; + Iterator iterator = provider.getNotifications().iterator(); + + if (!iterator.hasNext()) { + notificationManager.cancel(id); + return; + } + + NotificationItem top; + String ticker; + + if (notify == null) { + top = iterator.next(); + ticker = null; + } else { + top = notify; + ticker = top.getTitle(); + } + + Intent intent = top.getIntent(); + + NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(application); + + notificationBuilder.setSmallIcon(provider.getIcon()); + notificationBuilder.setTicker(ticker); + + if (!provider.canClearNotifications()) { + notificationBuilder.setOngoing(true); + } + + notificationBuilder.setContentTitle(top.getTitle()); + notificationBuilder.setContentText(top.getText()); + + notificationBuilder.setContentIntent(PendingIntent.getActivity(application, 0, intent, + PendingIntent.FLAG_UPDATE_CURRENT)); + + if (ticker != null) { + setNotificationDefaults(notificationBuilder, SettingsManager.eventsVibro(), provider.getSound(), provider.getStreamType()); + } + + notificationBuilder.setDeleteIntent(clearNotifications); + + notificationBuilder.setColor(accountPainter.getDefaultMainColor()); + + notify(id, notificationBuilder.build()); + } + + /** + * Sound, vibration and lightning flags. + * + * @param notificationBuilder + * @param streamType + */ + public void setNotificationDefaults(NotificationCompat.Builder notificationBuilder, boolean vibration, Uri sound, int streamType) { + notificationBuilder.setSound(sound, streamType); + notificationBuilder.setDefaults(0); + + int defaults = 0; + + if (vibration) { + if (SettingsManager.eventsIgnoreSystemVibro()) { + startVibration(); + } else { + defaults |= Notification.DEFAULT_VIBRATE; + + } + } + + if (SettingsManager.eventsLightning()) { + defaults |= Notification.DEFAULT_LIGHTS; + } + + notificationBuilder.setDefaults(defaults); + } + + public void startVibration() { + handler.post(startVibration); + } + + /** + * Chat was changed: + *
    + *
  • incoming message
  • + *
  • chat was opened
  • + *
  • account was changed
  • + *
+ *

+ * Update chat and persistent notifications. + * + * @param ticker message to be shown. + * @return + */ + private void updateMessageNotification(MessageItem ticker) { + updatePersistentNotification(); + + Notification messageNotification = messageNotificationCreator.notifyMessageNotification(messageNotifications, ticker); + + if (messageNotification != null) { + notify(MESSAGE_NOTIFICATION_ID, messageNotification); + } else { + notificationManager.cancel(MESSAGE_NOTIFICATION_ID); + } + } + + private void updatePersistentNotification() { + if (!SettingsManager.eventsPersistent()) { + return; + } + + int waiting = 0; + int connecting = 0; + int connected = 0; + + Collection accountList = AccountManager.getInstance().getAccounts(); + for (String account : accountList) { + ConnectionState state = AccountManager.getInstance().getAccount(account).getState(); + + if (RosterManager.getInstance().isRosterReceived(account)) { + connected++; + } else if (state == ConnectionState.connecting || state == ConnectionState.authentication) { + connecting++; + } else if (state == ConnectionState.waiting) { + waiting++; + } + } + + final Intent persistentIntent; + + if (waiting > 0 && application.isInitialized()) { + persistentIntent = ReconnectionActivity.createIntent(application); + } else { + persistentIntent = ContactList.createPersistentIntent(application); + } + + if (connected > 0) { + persistentNotificationBuilder.setColor(persistentNotificationColor); + persistentNotificationBuilder.setSmallIcon(R.drawable.ic_stat_online); + } else { + persistentNotificationBuilder.setColor(NotificationCompat.COLOR_DEFAULT); + persistentNotificationBuilder.setSmallIcon(R.drawable.ic_stat_offline); + } + + persistentNotificationBuilder.setContentText(getConnectionState(waiting, connecting, connected, accountList.size())); + persistentNotificationBuilder.setContentIntent(PendingIntent.getActivity(application, 0, persistentIntent, + PendingIntent.FLAG_UPDATE_CURRENT)); + + notify(PERSISTENT_NOTIFICATION_ID, persistentNotificationBuilder.build()); + } + + private String getConnectionState(int waiting, int connecting, int connected, int accountCount) { + + String accountQuantity; + String connectionState; + if (connected > 0) { + accountQuantity = StringUtils.getQuantityString( + application.getResources(), R.array.account_quantity, accountCount); + + String connectionFormat = StringUtils.getQuantityString( + application.getResources(), R.array.connection_state_connected, connected); + + connectionState = String.format(connectionFormat, connected, accountCount, accountQuantity); + + } else if (connecting > 0) { + + accountQuantity = StringUtils.getQuantityString( + application.getResources(), R.array.account_quantity, accountCount); + + String connectionFormat = StringUtils.getQuantityString( + application.getResources(), R.array.connection_state_connecting, connecting); + + connectionState = String.format(connectionFormat, connecting, accountCount, accountQuantity); + + } else if (waiting > 0 && application.isInitialized()) { + + accountQuantity = StringUtils.getQuantityString( + application.getResources(), R.array.account_quantity, accountCount); + + String connectionFormat = StringUtils.getQuantityString( + application.getResources(), R.array.connection_state_waiting, waiting); + + connectionState = String.format(connectionFormat, waiting, accountCount, accountQuantity); + + } else { + accountQuantity = StringUtils.getQuantityString( + application.getResources(), R.array.account_quantity_offline, accountCount); + connectionState = application.getString( + R.string.connection_state_offline, accountCount, accountQuantity); + } + return connectionState; + } + + private void notify(int id, Notification notification) { + LogManager.i(this, "Notification: " + id + + ", ticker: " + notification.tickerText + + ", sound: " + notification.sound + + ", vibro: " + (notification.defaults & Notification.DEFAULT_VIBRATE) + + ", light: " + (notification.defaults & Notification.DEFAULT_LIGHTS)); + try { + notificationManager.notify(id, notification); + } catch (SecurityException e) { + LogManager.exception(this, e); + } + } + + private MessageNotification getMessageNotification(String account, String user) { + for (MessageNotification messageNotification : messageNotifications) { + if (messageNotification.equals(account, user)) { + return messageNotification; + } + } + return null; + } + + public void onMessageNotification(MessageItem messageItem) { + MessageNotification messageNotification = getMessageNotification( + messageItem.getChat().getAccount(), messageItem.getChat().getUser()); + if (messageNotification == null) { + messageNotification = new MessageNotification( + messageItem.getChat().getAccount(), messageItem.getChat().getUser(), null, null, 0); + } else { + messageNotifications.remove(messageNotification); + } + messageNotification.addMessage(messageItem.getText()); + messageNotifications.add(messageNotification); + + final String account = messageNotification.getAccount(); + final String user = messageNotification.getUser(); + final String text = messageNotification.getText(); + final Date timestamp = messageNotification.getTimestamp(); + final int count = messageNotification.getCount(); + + if (AccountManager.getInstance().getArchiveMode(account) != ArchiveMode.dontStore) { + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + NotificationTable.getInstance().write(account, user, text, timestamp, count); + } + }); + } + + updateMessageNotification(messageItem); + } + + public void onCurrentChatMessageNotification(MessageItem messageItem) { + NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(application); + addEffects(notificationBuilder, messageItem); + notify(CURRENT_CHAT_MESSAGE_NOTIFICATION_ID, notificationBuilder.build()); + } + + /** + * Updates message notification. + */ + public void onMessageNotification() { + updateMessageNotification(null); + } + + public int getNotificationMessageCount(String account, String user) { + MessageNotification messageNotification = getMessageNotification( + account, user); + if (messageNotification == null) + return 0; + return messageNotification.getCount(); + } + + public void removeMessageNotification(final String account, final String user) { + MessageNotification messageNotification = getMessageNotification(account, user); + if (messageNotification == null) + return; + messageNotifications.remove(messageNotification); + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + NotificationTable.getInstance().remove(account, user); + } + }); + updateMessageNotification(null); + } + + /** + * Called when notifications was cleared by user. + */ + public void onClearNotifications() { + for (NotificationProvider provider : providers) + if (provider.canClearNotifications()) + provider.clearNotifications(); + messageNotifications.clear(); + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + NotificationTable.getInstance().clear(); + } + }); + updateMessageNotification(null); + } + + @Override + public void onAccountArchiveModeChanged(AccountItem accountItem) { + final String account = accountItem.getAccount(); + if (AccountManager.getInstance().getArchiveMode(account) != ArchiveMode.dontStore) + return; + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + NotificationTable.getInstance().removeAccount(account); + } + }); + } + + @Override + public void onAccountsChanged(Collection accounts) { + handler.post(this); + } + + @Override + public void onAccountRemoved(AccountItem accountItem) { + for (NotificationProvider notificationProvider : providers) { + if (notificationProvider instanceof AccountNotificationProvider) { + ((AccountNotificationProvider) notificationProvider) + .clearAccountNotifications(accountItem.getAccount()); + updateNotifications(notificationProvider, null); + } + } + } + + @Override + public void run() { + handler.removeCallbacks(this); + updateMessageNotification(null); + } + + public Notification getPersistentNotification() { + return persistentNotificationBuilder.build(); + } + + @Override + public void onClose() { + notificationManager.cancelAll(); + } } diff --git a/app/src/main/java/com/xabber/android/data/notification/NotificationProvider.java b/app/src/main/java/com/xabber/android/data/notification/NotificationProvider.java index 769f5db732..956606e6c6 100644 --- a/app/src/main/java/com/xabber/android/data/notification/NotificationProvider.java +++ b/app/src/main/java/com/xabber/android/data/notification/NotificationProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,41 +20,40 @@ /** * Provides list of notifications first of which should be shown. - * - * @author alexander.ivanov - * + * * @param + * @author alexander.ivanov */ public interface NotificationProvider { - /** - * @return List of notifications. - */ - Collection getNotifications(); - - /** - * @return Whether notification can be cleared. - */ - boolean canClearNotifications(); - - /** - * Clear notifications. - */ - void clearNotifications(); - - /** - * @return Sound for notification. - */ - Uri getSound(); - - /** - * @return Audio stream type for notification. - */ - int getStreamType(); - - /** - * @return Resource id with icon for notification bar. - */ - int getIcon(); + /** + * @return List of notifications. + */ + Collection getNotifications(); + + /** + * @return Whether notification can be cleared. + */ + boolean canClearNotifications(); + + /** + * Clear notifications. + */ + void clearNotifications(); + + /** + * @return Sound for notification. + */ + Uri getSound(); + + /** + * @return Audio stream type for notification. + */ + int getStreamType(); + + /** + * @return Resource id with icon for notification bar. + */ + int getIcon(); } diff --git a/app/src/main/java/com/xabber/android/data/notification/NotificationTable.java b/app/src/main/java/com/xabber/android/data/notification/NotificationTable.java index cf43a987ac..850036b113 100644 --- a/app/src/main/java/com/xabber/android/data/notification/NotificationTable.java +++ b/app/src/main/java/com/xabber/android/data/notification/NotificationTable.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -25,138 +25,138 @@ /** * Storage with notifications. - * + * * @author alexander.ivanov */ class NotificationTable extends AbstractEntityTable { - private static final class Fields implements AbstractEntityTable.Fields { - - private Fields() { - } - - /** - * Text message. - */ - public static final String TEXT = "text"; - - /** - * Time when last message was received. - */ - public static final String TIMESTAMP = "timestamp"; - - /** - * Count of not notified messages. - */ - public static final String COUNT = "count"; - - } - - private static final String NAME = "notifications"; - private static final String[] PROJECTION = new String[] { Fields.ACCOUNT, - Fields.USER, Fields.TEXT, Fields.TIMESTAMP, Fields.COUNT }; - - private final DatabaseManager databaseManager; - private SQLiteStatement writeStatement; - private final Object writeLock; - - private final static NotificationTable instance; - - static { - instance = new NotificationTable(DatabaseManager.getInstance()); - DatabaseManager.getInstance().addTable(instance); - } - - public static NotificationTable getInstance() { - return instance; - } - - private NotificationTable(DatabaseManager databaseManager) { - this.databaseManager = databaseManager; - writeStatement = null; - writeLock = new Object(); - } - - @Override - public void create(SQLiteDatabase db) { - String sql; - sql = "CREATE TABLE " + NAME + " (" + Fields.ACCOUNT + " TEXT," - + Fields.USER + " TEXT," + Fields.TEXT + " TEXT," - + Fields.TIMESTAMP + " INTEGER," + Fields.COUNT + " INTEGER);"; - DatabaseManager.execSQL(db, sql); - sql = "CREATE UNIQUE INDEX " + NAME + "_list ON " + NAME + " (" - + Fields.ACCOUNT + ", " + Fields.USER + " )"; - DatabaseManager.execSQL(db, sql); - } - - @Override - public void migrate(SQLiteDatabase db, int toVersion) { - super.migrate(db, toVersion); - String sql; - switch (toVersion) { - case 60: - sql = "CREATE TABLE notifications (" + "account TEXT," - + "user TEXT," + "text TEXT," + "timestamp INTEGER," - + "count INTEGER);"; - DatabaseManager.execSQL(db, sql); - sql = "CREATE UNIQUE INDEX notifications_list ON notifications (account, user);"; - DatabaseManager.execSQL(db, sql); - break; - default: - break; - } - } - - void write(String account, String user, String text, Date timeStamp, - int count) { - synchronized (writeLock) { - if (writeStatement == null) { - SQLiteDatabase db = databaseManager.getWritableDatabase(); - writeStatement = db.compileStatement("INSERT OR REPLACE INTO " - + NAME + " (" + Fields.ACCOUNT + ", " + Fields.USER - + ", " + Fields.TEXT + ", " + Fields.TIMESTAMP + ", " - + Fields.COUNT + ") VALUES " + "(?, ?, ?, ?, ?);"); - } - writeStatement.bindString(1, account); - writeStatement.bindString(2, user); - writeStatement.bindString(3, text); - writeStatement.bindLong(4, timeStamp.getTime()); - writeStatement.bindLong(5, count); - writeStatement.execute(); - } - } - - public void remove(String account, String user) { - SQLiteDatabase db = databaseManager.getWritableDatabase(); - db.delete(NAME, Fields.ACCOUNT + " = ? AND " + Fields.USER + " = ?", - new String[] { account, user }); - } - - @Override - protected String getTableName() { - return NAME; - } - - @Override - protected String[] getProjection() { - return PROJECTION; - } - - @Override - protected String getListOrder() { - return Fields.TIMESTAMP; - } - - static String getText(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.TEXT)); - } - - static Date getTimeStamp(Cursor cursor) { - return new Date(cursor.getLong(cursor.getColumnIndex(Fields.TIMESTAMP))); - } - - static int getCount(Cursor cursor) { - return cursor.getInt(cursor.getColumnIndex(Fields.COUNT)); - } + private static final class Fields implements AbstractEntityTable.Fields { + + private Fields() { + } + + /** + * Text message. + */ + public static final String TEXT = "text"; + + /** + * Time when last message was received. + */ + public static final String TIMESTAMP = "timestamp"; + + /** + * Count of not notified messages. + */ + public static final String COUNT = "count"; + + } + + private static final String NAME = "notifications"; + private static final String[] PROJECTION = new String[]{Fields.ACCOUNT, + Fields.USER, Fields.TEXT, Fields.TIMESTAMP, Fields.COUNT}; + + private final DatabaseManager databaseManager; + private SQLiteStatement writeStatement; + private final Object writeLock; + + private final static NotificationTable instance; + + static { + instance = new NotificationTable(DatabaseManager.getInstance()); + DatabaseManager.getInstance().addTable(instance); + } + + public static NotificationTable getInstance() { + return instance; + } + + private NotificationTable(DatabaseManager databaseManager) { + this.databaseManager = databaseManager; + writeStatement = null; + writeLock = new Object(); + } + + @Override + public void create(SQLiteDatabase db) { + String sql; + sql = "CREATE TABLE " + NAME + " (" + Fields.ACCOUNT + " TEXT," + + Fields.USER + " TEXT," + Fields.TEXT + " TEXT," + + Fields.TIMESTAMP + " INTEGER," + Fields.COUNT + " INTEGER);"; + DatabaseManager.execSQL(db, sql); + sql = "CREATE UNIQUE INDEX " + NAME + "_list ON " + NAME + " (" + + Fields.ACCOUNT + ", " + Fields.USER + " )"; + DatabaseManager.execSQL(db, sql); + } + + @Override + public void migrate(SQLiteDatabase db, int toVersion) { + super.migrate(db, toVersion); + String sql; + switch (toVersion) { + case 60: + sql = "CREATE TABLE notifications (" + "account TEXT," + + "user TEXT," + "text TEXT," + "timestamp INTEGER," + + "count INTEGER);"; + DatabaseManager.execSQL(db, sql); + sql = "CREATE UNIQUE INDEX notifications_list ON notifications (account, user);"; + DatabaseManager.execSQL(db, sql); + break; + default: + break; + } + } + + void write(String account, String user, String text, Date timeStamp, + int count) { + synchronized (writeLock) { + if (writeStatement == null) { + SQLiteDatabase db = databaseManager.getWritableDatabase(); + writeStatement = db.compileStatement("INSERT OR REPLACE INTO " + + NAME + " (" + Fields.ACCOUNT + ", " + Fields.USER + + ", " + Fields.TEXT + ", " + Fields.TIMESTAMP + ", " + + Fields.COUNT + ") VALUES " + "(?, ?, ?, ?, ?);"); + } + writeStatement.bindString(1, account); + writeStatement.bindString(2, user); + writeStatement.bindString(3, text); + writeStatement.bindLong(4, timeStamp.getTime()); + writeStatement.bindLong(5, count); + writeStatement.execute(); + } + } + + public void remove(String account, String user) { + SQLiteDatabase db = databaseManager.getWritableDatabase(); + db.delete(NAME, Fields.ACCOUNT + " = ? AND " + Fields.USER + " = ?", + new String[]{account, user}); + } + + @Override + protected String getTableName() { + return NAME; + } + + @Override + protected String[] getProjection() { + return PROJECTION; + } + + @Override + protected String getListOrder() { + return Fields.TIMESTAMP; + } + + static String getText(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.TEXT)); + } + + static Date getTimeStamp(Cursor cursor) { + return new Date(cursor.getLong(cursor.getColumnIndex(Fields.TIMESTAMP))); + } + + static int getCount(Cursor cursor) { + return cursor.getInt(cursor.getColumnIndex(Fields.COUNT)); + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/data/roster/AbstractContact.java b/app/src/main/java/com/xabber/android/data/roster/AbstractContact.java index ca6698940f..02c7fcf6c4 100644 --- a/app/src/main/java/com/xabber/android/data/roster/AbstractContact.java +++ b/app/src/main/java/com/xabber/android/data/roster/AbstractContact.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -30,76 +30,75 @@ /** * Basic contact representation. - * + * * @author alexander.ivanov - * */ public class AbstractContact extends BaseEntity { - public AbstractContact(String account, String user) { - super(account, user); - } - - /** - * vCard and roster can be used for name resolving. - * - * @return Verbose name. - */ - public String getName() { - String vCardName = VCardManager.getInstance().getName(user); - if (!"".equals(vCardName)) - return vCardName; - return user; - } - - public StatusMode getStatusMode() { - return PresenceManager.getInstance().getStatusMode(account, user); - } - - public String getStatusText() { - return PresenceManager.getInstance().getStatusText(account, user); - } - - public ClientSoftware getClientSoftware() { - ResourceItem resourceItem = PresenceManager.getInstance() - .getResourceItem(account, user); - if (resourceItem == null) - return ClientSoftware.unknown; - ClientInfo clientInfo = CapabilitiesManager.getInstance() - .getClientInfo(account, resourceItem.getUser(user)); - if (clientInfo == null) - return ClientSoftware.unknown; - return clientInfo.getClientSoftware(); - } - - public Collection getGroups() { - return Collections.emptyList(); - } - - public Drawable getAvatar() { - return AvatarManager.getInstance().getUserAvatar(user); - - } - - /** - * @return Cached avatar's drawable for contact list. - */ - public Drawable getAvatarForContactList() { - return AvatarManager.getInstance().getUserAvatarForContactList(user); - } - - /** - * @return Account's color level. - */ - public int getColorLevel() { - return AccountManager.getInstance().getColorLevel(account); - } - - /** - * @return Whether contact is connected. - */ - public boolean isConnected() { - return true; - } + public AbstractContact(String account, String user) { + super(account, user); + } + + /** + * vCard and roster can be used for name resolving. + * + * @return Verbose name. + */ + public String getName() { + String vCardName = VCardManager.getInstance().getName(user); + if (!"".equals(vCardName)) + return vCardName; + return user; + } + + public StatusMode getStatusMode() { + return PresenceManager.getInstance().getStatusMode(account, user); + } + + public String getStatusText() { + return PresenceManager.getInstance().getStatusText(account, user); + } + + public ClientSoftware getClientSoftware() { + ResourceItem resourceItem = PresenceManager.getInstance() + .getResourceItem(account, user); + if (resourceItem == null) + return ClientSoftware.unknown; + ClientInfo clientInfo = CapabilitiesManager.getInstance() + .getClientInfo(account, resourceItem.getUser(user)); + if (clientInfo == null) + return ClientSoftware.unknown; + return clientInfo.getClientSoftware(); + } + + public Collection getGroups() { + return Collections.emptyList(); + } + + public Drawable getAvatar() { + return AvatarManager.getInstance().getUserAvatar(user); + + } + + /** + * @return Cached avatar's drawable for contact list. + */ + public Drawable getAvatarForContactList() { + return AvatarManager.getInstance().getUserAvatarForContactList(user); + } + + /** + * @return Account's color level. + */ + public int getColorLevel() { + return AccountManager.getInstance().getColorLevel(account); + } + + /** + * @return Whether contact is connected. + */ + public boolean isConnected() { + return true; + } } diff --git a/app/src/main/java/com/xabber/android/data/roster/Group.java b/app/src/main/java/com/xabber/android/data/roster/Group.java index 3aa96d90b6..fed72f42ee 100644 --- a/app/src/main/java/com/xabber/android/data/roster/Group.java +++ b/app/src/main/java/com/xabber/android/data/roster/Group.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,12 +16,11 @@ /** * Named contact's group. - * + * * @author alexander.ivanov - * */ public interface Group { - public String getName(); + String getName(); } diff --git a/app/src/main/java/com/xabber/android/data/roster/GroupConfiguration.java b/app/src/main/java/com/xabber/android/data/roster/GroupConfiguration.java index 35fb0b8cd1..bc18c55bdc 100644 --- a/app/src/main/java/com/xabber/android/data/roster/GroupConfiguration.java +++ b/app/src/main/java/com/xabber/android/data/roster/GroupConfiguration.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,42 +16,41 @@ /** * Group's appearance settings. - * + * * @author alexander.ivanov - * */ class GroupConfiguration { - /** - * Whether group must be expanded. - */ - private boolean expanded; - - /** - * Show offline contact mode. - */ - private ShowOfflineMode showOfflineMode; - - public GroupConfiguration() { - super(); - expanded = true; - showOfflineMode = ShowOfflineMode.normal; - } - - public boolean isExpanded() { - return expanded; - } - - public void setExpanded(boolean expanded) { - this.expanded = expanded; - } - - public ShowOfflineMode getShowOfflineMode() { - return showOfflineMode; - } - - public void setShowOfflineMode(ShowOfflineMode showOfflineMode) { - this.showOfflineMode = showOfflineMode; - } + /** + * Whether group must be expanded. + */ + private boolean expanded; + + /** + * Show offline contact mode. + */ + private ShowOfflineMode showOfflineMode; + + public GroupConfiguration() { + super(); + expanded = true; + showOfflineMode = ShowOfflineMode.normal; + } + + public boolean isExpanded() { + return expanded; + } + + public void setExpanded(boolean expanded) { + this.expanded = expanded; + } + + public ShowOfflineMode getShowOfflineMode() { + return showOfflineMode; + } + + public void setShowOfflineMode(ShowOfflineMode showOfflineMode) { + this.showOfflineMode = showOfflineMode; + } } diff --git a/app/src/main/java/com/xabber/android/data/roster/GroupManager.java b/app/src/main/java/com/xabber/android/data/roster/GroupManager.java index 9f766c1e92..96af33b400 100644 --- a/app/src/main/java/com/xabber/android/data/roster/GroupManager.java +++ b/app/src/main/java/com/xabber/android/data/roster/GroupManager.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,6 +16,7 @@ import android.database.Cursor; +import com.xabber.android.R; import com.xabber.android.data.Application; import com.xabber.android.data.OnLoadListener; import com.xabber.android.data.account.AccountItem; @@ -23,174 +24,172 @@ import com.xabber.android.data.account.OnAccountRemovedListener; import com.xabber.android.data.entity.NestedMap; import com.xabber.android.data.entity.NestedMap.Entry; -import com.xabber.androiddev.R; public class GroupManager implements OnLoadListener, OnAccountRemovedListener, - GroupStateProvider { - - /** - * Reserved group name for the rooms. - */ - public static final String IS_ROOM = "com.xabber.android.data.IS_ROOM"; - - /** - * Reserved group name for active chat group. - */ - public static final String ACTIVE_CHATS = "com.xabber.android.data.ACTIVE_CHATS"; - - /** - * Reserved group name to store information about group "out of groups". - */ - public static final String NO_GROUP = "com.xabber.android.data.NO_GROUP"; - - /** - * Group name used to store information about account itself. - */ - public static final String IS_ACCOUNT = "com.xabber.android.data.IS_ACCOUNT"; - - /** - * Account name used to store information that don't belong to any account. - */ - public static final String NO_ACCOUNT = "com.xabber.android.data.NO_ACCOUNT"; - - /** - * List of settings for roster groups in accounts. - */ - private final NestedMap groupConfigurations; - - private final static GroupManager instance; - - static { - instance = new GroupManager(); - Application.getInstance().addManager(instance); - } - - public static GroupManager getInstance() { - return instance; - } - - private GroupManager() { - groupConfigurations = new NestedMap(); - } - - @Override - public void onLoad() { - final NestedMap groupConfigurations = new NestedMap(); - Cursor cursor = GroupTable.getInstance().list(); - try { - if (cursor.moveToFirst()) { - do { - GroupConfiguration rosterConfiguration = new GroupConfiguration(); - rosterConfiguration.setExpanded(GroupTable - .isExpanded(cursor)); - rosterConfiguration.setShowOfflineMode(GroupTable - .getShowOfflineMode(cursor)); - groupConfigurations.put(GroupTable.getAccount(cursor), - GroupTable.getGroup(cursor), rosterConfiguration); - } while (cursor.moveToNext()); - } - } finally { - cursor.close(); - } - Application.getInstance().runOnUiThread(new Runnable() { - @Override - public void run() { - onLoaded(groupConfigurations); - } - }); - } - - private void onLoaded(NestedMap groupConfigurations) { - this.groupConfigurations.addAll(groupConfigurations); - } - - @Override - public void onAccountRemoved(AccountItem accountItem) { - groupConfigurations.clear(accountItem.getAccount()); - } - - /** - * @return Group's name to be display. - * @see {@link #IS_ROOM}, {@link #ACTIVE_CHATS}, {@link #NO_GROUP}, - * {@link #IS_ACCOUNT}, {@link #NO_ACCOUNT}. - */ - public String getGroupName(String account, String group) { - if (group == GroupManager.NO_GROUP) - return Application.getInstance().getString(R.string.group_none); - else if (group == GroupManager.IS_ROOM) - return Application.getInstance().getString(R.string.group_room); - else if (group == GroupManager.ACTIVE_CHATS) - return Application.getInstance().getString( - R.string.group_active_chat); - else if (group == GroupManager.IS_ACCOUNT) - return AccountManager.getInstance().getVerboseName(account); - return group; - } - - @Override - public boolean isExpanded(String account, String group) { - GroupConfiguration configuration = groupConfigurations.get(account, - group); - if (configuration == null) - return true; - return configuration.isExpanded(); - } - - @Override - public ShowOfflineMode getShowOfflineMode(String account, String group) { - GroupConfiguration configuration = groupConfigurations.get(account, - group); - if (configuration == null) - return ShowOfflineMode.normal; - return configuration.getShowOfflineMode(); - } - - @Override - public void setExpanded(String account, String group, boolean expanded) { - GroupConfiguration configuration = groupConfigurations.get(account, - group); - if (configuration == null) { - configuration = new GroupConfiguration(); - groupConfigurations.put(account, group, configuration); - } - configuration.setExpanded(expanded); - requestToWriteGroup(account, group, configuration.isExpanded(), - configuration.getShowOfflineMode()); - } - - @Override - public void setShowOfflineMode(String account, String group, - ShowOfflineMode showOfflineMode) { - GroupConfiguration configuration = groupConfigurations.get(account, - group); - if (configuration == null) { - configuration = new GroupConfiguration(); - groupConfigurations.put(account, group, configuration); - } - configuration.setShowOfflineMode(showOfflineMode); - requestToWriteGroup(account, group, configuration.isExpanded(), - configuration.getShowOfflineMode()); - } - - /** - * Reset all show offline modes. - */ - public void resetShowOfflineModes() { - for (Entry entry : groupConfigurations) - if (entry.getValue().getShowOfflineMode() != ShowOfflineMode.normal) - setShowOfflineMode(entry.getFirst(), entry.getSecond(), - ShowOfflineMode.normal); - } - - private void requestToWriteGroup(final String account, final String group, - final boolean expanded, final ShowOfflineMode showOfflineMode) { - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - GroupTable.getInstance().write(account, group, expanded, - showOfflineMode); - } - }); - } + GroupStateProvider { + + /** + * Reserved group name for the rooms. + */ + public static final String IS_ROOM = "com.xabber.android.data.IS_ROOM"; + + /** + * Reserved group name for active chat group. + */ + public static final String ACTIVE_CHATS = "com.xabber.android.data.ACTIVE_CHATS"; + + /** + * Reserved group name to store information about group "out of groups". + */ + public static final String NO_GROUP = "com.xabber.android.data.NO_GROUP"; + + /** + * Group name used to store information about account itself. + */ + public static final String IS_ACCOUNT = "com.xabber.android.data.IS_ACCOUNT"; + + /** + * Account name used to store information that don't belong to any account. + */ + public static final String NO_ACCOUNT = "com.xabber.android.data.NO_ACCOUNT"; + private final static GroupManager instance; + + static { + instance = new GroupManager(); + Application.getInstance().addManager(instance); + } + + /** + * List of settings for roster groups in accounts. + */ + private final NestedMap groupConfigurations; + + private GroupManager() { + groupConfigurations = new NestedMap(); + } + + public static GroupManager getInstance() { + return instance; + } + + @Override + public void onLoad() { + final NestedMap groupConfigurations = new NestedMap(); + Cursor cursor = GroupTable.getInstance().list(); + try { + if (cursor.moveToFirst()) { + do { + GroupConfiguration rosterConfiguration = new GroupConfiguration(); + rosterConfiguration.setExpanded(GroupTable + .isExpanded(cursor)); + rosterConfiguration.setShowOfflineMode(GroupTable + .getShowOfflineMode(cursor)); + groupConfigurations.put(GroupTable.getAccount(cursor), + GroupTable.getGroup(cursor), rosterConfiguration); + } while (cursor.moveToNext()); + } + } finally { + cursor.close(); + } + Application.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + onLoaded(groupConfigurations); + } + }); + } + + private void onLoaded(NestedMap groupConfigurations) { + this.groupConfigurations.addAll(groupConfigurations); + } + + @Override + public void onAccountRemoved(AccountItem accountItem) { + groupConfigurations.clear(accountItem.getAccount()); + } + + /** + * @return Group's name to be display. + * @see {@link #IS_ROOM}, {@link #ACTIVE_CHATS}, {@link #NO_GROUP}, + * {@link #IS_ACCOUNT}, {@link #NO_ACCOUNT}. + */ + public String getGroupName(String account, String group) { + if (group == GroupManager.NO_GROUP) + return Application.getInstance().getString(R.string.group_none); + else if (group == GroupManager.IS_ROOM) + return Application.getInstance().getString(R.string.group_room); + else if (group == GroupManager.ACTIVE_CHATS) + return Application.getInstance().getString( + R.string.group_active_chat); + else if (group == GroupManager.IS_ACCOUNT) + return AccountManager.getInstance().getVerboseName(account); + return group; + } + + @Override + public boolean isExpanded(String account, String group) { + GroupConfiguration configuration = groupConfigurations.get(account, + group); + if (configuration == null) + return true; + return configuration.isExpanded(); + } + + @Override + public ShowOfflineMode getShowOfflineMode(String account, String group) { + GroupConfiguration configuration = groupConfigurations.get(account, + group); + if (configuration == null) + return ShowOfflineMode.normal; + return configuration.getShowOfflineMode(); + } + + @Override + public void setExpanded(String account, String group, boolean expanded) { + GroupConfiguration configuration = groupConfigurations.get(account, + group); + if (configuration == null) { + configuration = new GroupConfiguration(); + groupConfigurations.put(account, group, configuration); + } + configuration.setExpanded(expanded); + requestToWriteGroup(account, group, configuration.isExpanded(), + configuration.getShowOfflineMode()); + } + + @Override + public void setShowOfflineMode(String account, String group, + ShowOfflineMode showOfflineMode) { + GroupConfiguration configuration = groupConfigurations.get(account, + group); + if (configuration == null) { + configuration = new GroupConfiguration(); + groupConfigurations.put(account, group, configuration); + } + configuration.setShowOfflineMode(showOfflineMode); + requestToWriteGroup(account, group, configuration.isExpanded(), + configuration.getShowOfflineMode()); + } + + /** + * Reset all show offline modes. + */ + public void resetShowOfflineModes() { + for (Entry entry : groupConfigurations) + if (entry.getValue().getShowOfflineMode() != ShowOfflineMode.normal) + setShowOfflineMode(entry.getFirst(), entry.getSecond(), + ShowOfflineMode.normal); + } + + private void requestToWriteGroup(final String account, final String group, + final boolean expanded, final ShowOfflineMode showOfflineMode) { + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + GroupTable.getInstance().write(account, group, expanded, + showOfflineMode); + } + }); + } } diff --git a/app/src/main/java/com/xabber/android/data/roster/GroupStateProvider.java b/app/src/main/java/com/xabber/android/data/roster/GroupStateProvider.java index d761f1a6a6..a88e7acdec 100644 --- a/app/src/main/java/com/xabber/android/data/roster/GroupStateProvider.java +++ b/app/src/main/java/com/xabber/android/data/roster/GroupStateProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,43 +16,43 @@ /** * Provide information about group. - * + * * @author alexander.ivanov */ public interface GroupStateProvider { - /** - * @param account - * @param group - * @return Whether specified group in specified account is expanded. - */ - boolean isExpanded(String account, String group); + /** + * @param account + * @param group + * @return Whether specified group in specified account is expanded. + */ + boolean isExpanded(String account, String group); - /** - * @param account - * @param group - * @return Whether to show offline contacts for specified group in specified - * account. - */ - ShowOfflineMode getShowOfflineMode(String account, String group); + /** + * @param account + * @param group + * @return Whether to show offline contacts for specified group in specified + * account. + */ + ShowOfflineMode getShowOfflineMode(String account, String group); - /** - * Sets whether group in specified account is expanded. - * - * @param account - * @param group - * @param expanded - */ - void setExpanded(String account, String group, boolean expanded); + /** + * Sets whether group in specified account is expanded. + * + * @param account + * @param group + * @param expanded + */ + void setExpanded(String account, String group, boolean expanded); - /** - * Sets whether to show offline contacts for specified group. - * - * @param account - * @param group - * @param show - */ - void setShowOfflineMode(String account, String group, - ShowOfflineMode showOfflineMode); + /** + * Sets whether to show offline contacts for specified group. + * + * @param account + * @param group + * @param show + */ + void setShowOfflineMode(String account, String group, + ShowOfflineMode showOfflineMode); } diff --git a/app/src/main/java/com/xabber/android/data/roster/GroupTable.java b/app/src/main/java/com/xabber/android/data/roster/GroupTable.java index 17d94cac66..63a5c992cb 100644 --- a/app/src/main/java/com/xabber/android/data/roster/GroupTable.java +++ b/app/src/main/java/com/xabber/android/data/roster/GroupTable.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -23,171 +23,171 @@ /** * Storage with contact list group settings. - * + * * @author alexander.ivanov */ class GroupTable extends AbstractAccountTable { - private static final class Fields implements AbstractAccountTable.Fields { - - private Fields() { - } - - public static final String GROUP_NAME = "group_name"; - - /** - * Whether group is expanded. - */ - public static final String EXPANDED = "expanded"; - - /** - * Show offline contact mode. - */ - public static final String OFFLINE = "offline"; - - } - - private static final String NAME = "groups"; - private static final String[] PROJECTION = new String[] { Fields.ACCOUNT, - Fields.GROUP_NAME, Fields.EXPANDED, Fields.OFFLINE }; - static final boolean DEFAULT_EXPANDED = true; - - private final DatabaseManager databaseManager; - private SQLiteStatement writeStatement; - private final Object writeLock; - - private final static GroupTable instance; - - static { - instance = new GroupTable(DatabaseManager.getInstance()); - DatabaseManager.getInstance().addTable(instance); - } - - public static GroupTable getInstance() { - return instance; - } - - private GroupTable(DatabaseManager databaseManager) { - this.databaseManager = databaseManager; - writeStatement = null; - writeLock = new Object(); - } - - @Override - public void create(SQLiteDatabase db) { - String sql = "CREATE TABLE " + NAME + " (" + Fields.ACCOUNT + " TEXT," - + Fields.GROUP_NAME + " TEXT," + Fields.EXPANDED + " BOOLEAN," - + Fields.OFFLINE + " INTEGER);"; - DatabaseManager.execSQL(db, sql); - sql = "CREATE UNIQUE INDEX " + NAME + "_group ON " + NAME + " (" - + Fields.ACCOUNT + ", " + Fields.GROUP_NAME + ");"; - DatabaseManager.execSQL(db, sql); - } - - @Override - public void migrate(SQLiteDatabase db, int toVersion) { - super.migrate(db, toVersion); - String sql; - switch (toVersion) { - case 7: - sql = "CREATE TABLE groups (" + "_id INTEGER PRIMARY KEY," - + "account TEXT," + "group_name TEXT," - + "expanded BOOLEAN);"; - DatabaseManager.execSQL(db, sql); - sql = "CREATE UNIQUE INDEX groups_group ON groups " - + "(account, group_name);"; - DatabaseManager.execSQL(db, sql); - break; - case 13: - sql = "ALTER TABLE groups ADD COLUMN " + "offline BOOLEAN;"; - DatabaseManager.execSQL(db, sql); - sql = "UPDATE groups SET offline = 0;"; - DatabaseManager.execSQL(db, sql); - break; - case 14: - DatabaseManager.dropTable(db, "groups"); - sql = "CREATE TABLE groups (" + "account TEXT," - + "group_name TEXT," + "expanded BOOLEAN," - + "offline BOOLEAN);"; - DatabaseManager.execSQL(db, sql); - sql = "CREATE UNIQUE INDEX groups_group ON groups " - + "(account, group_name);"; - DatabaseManager.execSQL(db, sql); - break; - case 33: - DatabaseManager.renameTable(db, "groups", "groups_"); - sql = "CREATE TABLE groups (" + "account TEXT," - + "group_name TEXT," + "expanded BOOLEAN," - + "offline INTEGER);"; - DatabaseManager.execSQL(db, sql); - sql = "INSERT INTO groups SELECT " - + "account, group_name, expanded, offline FROM groups_;"; - DatabaseManager.execSQL(db, sql); - DatabaseManager.dropTable(db, "groups_"); - sql = "CREATE UNIQUE INDEX groups_group ON groups " - + "(account, group_name);"; - DatabaseManager.execSQL(db, sql); - break; - default: - break; - } - } - - void write(String account, String group, boolean expanded, - ShowOfflineMode showOfflineMode) { - if (account == null || group == null) - throw new IllegalArgumentException(); - synchronized (writeLock) { - if (writeStatement == null) { - SQLiteDatabase db = databaseManager.getWritableDatabase(); - writeStatement = db.compileStatement("INSERT OR REPLACE INTO " - + NAME + " (" + Fields.ACCOUNT + ", " - + Fields.GROUP_NAME + ", " + Fields.EXPANDED + ", " - + Fields.OFFLINE + ") VALUES (?, ?, ?, ?);"); - } - writeStatement.bindString(1, account); - writeStatement.bindString(2, group); - writeStatement.bindLong(3, expanded ? 1 : 0); - if (showOfflineMode == ShowOfflineMode.never) - writeStatement.bindLong(4, -1); - else if (showOfflineMode == ShowOfflineMode.normal) - writeStatement.bindLong(4, 0); - else if (showOfflineMode == ShowOfflineMode.always) - writeStatement.bindLong(4, 1); - else - throw new IllegalStateException(); - writeStatement.execute(); - } - } - - @Override - protected String getTableName() { - return NAME; - } - - @Override - protected String[] getProjection() { - return PROJECTION; - } - - static String getGroup(Cursor cursor) { - return cursor.getString(cursor.getColumnIndex(Fields.GROUP_NAME)); - } - - static boolean isExpanded(Cursor cursor) { - return cursor.getInt(cursor.getColumnIndex(Fields.EXPANDED)) != 0; - } - - static ShowOfflineMode getShowOfflineMode(Cursor cursor) { - int value = cursor.getInt(cursor.getColumnIndex(Fields.OFFLINE)); - if (value == -1) - return ShowOfflineMode.never; - else if (value == 0) - return ShowOfflineMode.normal; - else if (value == 1) - return ShowOfflineMode.always; - else - throw new IllegalStateException(); - } + private static final class Fields implements AbstractAccountTable.Fields { + + private Fields() { + } + + public static final String GROUP_NAME = "group_name"; + + /** + * Whether group is expanded. + */ + public static final String EXPANDED = "expanded"; + + /** + * Show offline contact mode. + */ + public static final String OFFLINE = "offline"; + + } + + private static final String NAME = "groups"; + private static final String[] PROJECTION = new String[]{Fields.ACCOUNT, + Fields.GROUP_NAME, Fields.EXPANDED, Fields.OFFLINE}; + static final boolean DEFAULT_EXPANDED = true; + + private final DatabaseManager databaseManager; + private SQLiteStatement writeStatement; + private final Object writeLock; + + private final static GroupTable instance; + + static { + instance = new GroupTable(DatabaseManager.getInstance()); + DatabaseManager.getInstance().addTable(instance); + } + + public static GroupTable getInstance() { + return instance; + } + + private GroupTable(DatabaseManager databaseManager) { + this.databaseManager = databaseManager; + writeStatement = null; + writeLock = new Object(); + } + + @Override + public void create(SQLiteDatabase db) { + String sql = "CREATE TABLE " + NAME + " (" + Fields.ACCOUNT + " TEXT," + + Fields.GROUP_NAME + " TEXT," + Fields.EXPANDED + " BOOLEAN," + + Fields.OFFLINE + " INTEGER);"; + DatabaseManager.execSQL(db, sql); + sql = "CREATE UNIQUE INDEX " + NAME + "_group ON " + NAME + " (" + + Fields.ACCOUNT + ", " + Fields.GROUP_NAME + ");"; + DatabaseManager.execSQL(db, sql); + } + + @Override + public void migrate(SQLiteDatabase db, int toVersion) { + super.migrate(db, toVersion); + String sql; + switch (toVersion) { + case 7: + sql = "CREATE TABLE groups (" + "_id INTEGER PRIMARY KEY," + + "account TEXT," + "group_name TEXT," + + "expanded BOOLEAN);"; + DatabaseManager.execSQL(db, sql); + sql = "CREATE UNIQUE INDEX groups_group ON groups " + + "(account, group_name);"; + DatabaseManager.execSQL(db, sql); + break; + case 13: + sql = "ALTER TABLE groups ADD COLUMN " + "offline BOOLEAN;"; + DatabaseManager.execSQL(db, sql); + sql = "UPDATE groups SET offline = 0;"; + DatabaseManager.execSQL(db, sql); + break; + case 14: + DatabaseManager.dropTable(db, "groups"); + sql = "CREATE TABLE groups (" + "account TEXT," + + "group_name TEXT," + "expanded BOOLEAN," + + "offline BOOLEAN);"; + DatabaseManager.execSQL(db, sql); + sql = "CREATE UNIQUE INDEX groups_group ON groups " + + "(account, group_name);"; + DatabaseManager.execSQL(db, sql); + break; + case 33: + DatabaseManager.renameTable(db, "groups", "groups_"); + sql = "CREATE TABLE groups (" + "account TEXT," + + "group_name TEXT," + "expanded BOOLEAN," + + "offline INTEGER);"; + DatabaseManager.execSQL(db, sql); + sql = "INSERT INTO groups SELECT " + + "account, group_name, expanded, offline FROM groups_;"; + DatabaseManager.execSQL(db, sql); + DatabaseManager.dropTable(db, "groups_"); + sql = "CREATE UNIQUE INDEX groups_group ON groups " + + "(account, group_name);"; + DatabaseManager.execSQL(db, sql); + break; + default: + break; + } + } + + void write(String account, String group, boolean expanded, + ShowOfflineMode showOfflineMode) { + if (account == null || group == null) + throw new IllegalArgumentException(); + synchronized (writeLock) { + if (writeStatement == null) { + SQLiteDatabase db = databaseManager.getWritableDatabase(); + writeStatement = db.compileStatement("INSERT OR REPLACE INTO " + + NAME + " (" + Fields.ACCOUNT + ", " + + Fields.GROUP_NAME + ", " + Fields.EXPANDED + ", " + + Fields.OFFLINE + ") VALUES (?, ?, ?, ?);"); + } + writeStatement.bindString(1, account); + writeStatement.bindString(2, group); + writeStatement.bindLong(3, expanded ? 1 : 0); + if (showOfflineMode == ShowOfflineMode.never) + writeStatement.bindLong(4, -1); + else if (showOfflineMode == ShowOfflineMode.normal) + writeStatement.bindLong(4, 0); + else if (showOfflineMode == ShowOfflineMode.always) + writeStatement.bindLong(4, 1); + else + throw new IllegalStateException(); + writeStatement.execute(); + } + } + + @Override + protected String getTableName() { + return NAME; + } + + @Override + protected String[] getProjection() { + return PROJECTION; + } + + static String getGroup(Cursor cursor) { + return cursor.getString(cursor.getColumnIndex(Fields.GROUP_NAME)); + } + + static boolean isExpanded(Cursor cursor) { + return cursor.getInt(cursor.getColumnIndex(Fields.EXPANDED)) != 0; + } + + static ShowOfflineMode getShowOfflineMode(Cursor cursor) { + int value = cursor.getInt(cursor.getColumnIndex(Fields.OFFLINE)); + if (value == -1) + return ShowOfflineMode.never; + else if (value == 0) + return ShowOfflineMode.normal; + else if (value == 1) + return ShowOfflineMode.always; + else + throw new IllegalStateException(); + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/data/roster/OnContactChangedListener.java b/app/src/main/java/com/xabber/android/data/roster/OnContactChangedListener.java index 90350f3aeb..613595402c 100644 --- a/app/src/main/java/com/xabber/android/data/roster/OnContactChangedListener.java +++ b/app/src/main/java/com/xabber/android/data/roster/OnContactChangedListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -21,17 +21,16 @@ /** * Listener for contact change. - * + * * @author alexander.ivanov - * */ public interface OnContactChangedListener extends BaseUIListener { - /** - * Contacts changed. - * - * @param entities - */ - public void onContactsChanged(Collection entities); + /** + * Contacts changed. + * + * @param entities + */ + void onContactsChanged(Collection entities); } diff --git a/app/src/main/java/com/xabber/android/data/roster/OnRosterChangedListener.java b/app/src/main/java/com/xabber/android/data/roster/OnRosterChangedListener.java index f4d8b35403..3655d03259 100644 --- a/app/src/main/java/com/xabber/android/data/roster/OnRosterChangedListener.java +++ b/app/src/main/java/com/xabber/android/data/roster/OnRosterChangedListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -21,45 +21,44 @@ /** * Listener for any contact changes. - * + * * @author alexander.ivanov - * */ public interface OnRosterChangedListener extends BaseManagerInterface { - /** - * Roster update received. - * - * @param addedGroups - * @param addedContacts - * @param renamedContacts - * @param addedGroupReference - * @param removedGroupReference - * @param removedContacts - * @param removedGroups - */ - public void onRosterUpdate( - Collection addedGroups, - Map addedContacts, - Map renamedContacts, - Map> addedGroupReference, - Map> removedGroupReference, - Collection removedContacts, - Collection removedGroups); + /** + * Roster update received. + * + * @param addedGroups + * @param addedContacts + * @param renamedContacts + * @param addedGroupReference + * @param removedGroupReference + * @param removedContacts + * @param removedGroups + */ + void onRosterUpdate( + Collection addedGroups, + Map addedContacts, + Map renamedContacts, + Map> addedGroupReference, + Map> removedGroupReference, + Collection removedContacts, + Collection removedGroups); - /** - * Contact's presence has been changed. - * - * @param rosterContact - */ - public void onPresenceChanged(Collection rosterContact); + /** + * Contact's presence has been changed. + * + * @param rosterContact + */ + void onPresenceChanged(Collection rosterContact); - /** - * Contact's structured name has been changed. - * - * @param rosterContact - */ - public void onContactStructuredInfoChanged(RosterContact rosterContact, - StructuredName structuredName); + /** + * Contact's structured name has been changed. + * + * @param rosterContact + */ + void onContactStructuredInfoChanged(RosterContact rosterContact, + StructuredName structuredName); } diff --git a/app/src/main/java/com/xabber/android/data/roster/OnRosterReceivedListener.java b/app/src/main/java/com/xabber/android/data/roster/OnRosterReceivedListener.java index 4a4951f51d..ec9f79350e 100644 --- a/app/src/main/java/com/xabber/android/data/roster/OnRosterReceivedListener.java +++ b/app/src/main/java/com/xabber/android/data/roster/OnRosterReceivedListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -19,17 +19,16 @@ /** * Listen for roster to be received. - * + * * @author alexander.ivanov - * */ public interface OnRosterReceivedListener extends BaseManagerInterface { - /** - * Roster was received. - * - * @param accountItem - */ - void onRosterReceived(AccountItem accountItem); + /** + * Roster was received. + * + * @param accountItem + */ + void onRosterReceived(AccountItem accountItem); } diff --git a/app/src/main/java/com/xabber/android/data/roster/OnStatusChangeListener.java b/app/src/main/java/com/xabber/android/data/roster/OnStatusChangeListener.java index 86299b3dda..dbe7921a22 100644 --- a/app/src/main/java/com/xabber/android/data/roster/OnStatusChangeListener.java +++ b/app/src/main/java/com/xabber/android/data/roster/OnStatusChangeListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -19,33 +19,32 @@ /** * Listen for changes in presence. - * + * * @author alexander.ivanov - * */ public interface OnStatusChangeListener extends BaseManagerInterface { - /** - * Notify when only status text changed. - * - * @param account - * @param bareAddress - * @param resource - * @param statusText - */ - void onStatusChanged(String account, String bareAddress, String resource, - String statusText); + /** + * Notify when only status text changed. + * + * @param account + * @param bareAddress + * @param resource + * @param statusText + */ + void onStatusChanged(String account, String bareAddress, String resource, + String statusText); - /** - * Notify when status mode changed. - * - * @param account - * @param bareAddress - * @param resource - * @param statusMode - * @param statusText - */ - void onStatusChanged(String account, String bareAddress, String resource, - StatusMode statusMode, String statusText); + /** + * Notify when status mode changed. + * + * @param account + * @param bareAddress + * @param resource + * @param statusMode + * @param statusText + */ + void onStatusChanged(String account, String bareAddress, String resource, + StatusMode statusMode, String statusText); } diff --git a/app/src/main/java/com/xabber/android/data/roster/PresenceManager.java b/app/src/main/java/com/xabber/android/data/roster/PresenceManager.java index 587a00529a..1d03631ca1 100644 --- a/app/src/main/java/com/xabber/android/data/roster/PresenceManager.java +++ b/app/src/main/java/com/xabber/android/data/roster/PresenceManager.java @@ -1,33 +1,20 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data.roster; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; - -import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smack.packet.Packet; -import org.jivesoftware.smack.packet.Presence; -import org.jivesoftware.smack.packet.Presence.Type; -import org.jivesoftware.smack.packet.RosterPacket; -import org.jivesoftware.smack.packet.RosterPacket.ItemType; -import org.jivesoftware.smack.util.StringUtils; - +import com.xabber.android.R; import com.xabber.android.data.Application; import com.xabber.android.data.NetworkException; import com.xabber.android.data.OnLoadListener; @@ -43,352 +30,361 @@ import com.xabber.android.data.extension.archive.OnArchiveModificationsReceivedListener; import com.xabber.android.data.notification.EntityNotificationProvider; import com.xabber.android.data.notification.NotificationManager; -import com.xabber.androiddev.R; import com.xabber.xmpp.address.Jid; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smack.packet.Presence; +import org.jivesoftware.smack.packet.Presence.Type; +import org.jivesoftware.smack.packet.RosterPacket; +import org.jivesoftware.smack.packet.RosterPacket.ItemType; +import org.jivesoftware.smack.util.StringUtils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; + /** * Process contact's presence information. - * + * * @author alexander.ivanov - * */ public class PresenceManager implements OnArchiveModificationsReceivedListener, - OnPacketListener, OnLoadListener, OnAccountDisabledListener, - OnDisconnectListener { - - private final EntityNotificationProvider subscriptionRequestProvider; - - /** - * List of account with requested subscriptions for auto accept incoming - * subscription request. - */ - private final HashMap> requestedSubscriptions; - - /** - * Presence container for bare address in account. - */ - private final NestedMap presenceContainers; - - /** - * Account ready to send / update its presence information. - */ - private final ArrayList readyAccounts; - - private final static PresenceManager instance; - - static { - instance = new PresenceManager(); - Application.getInstance().addManager(instance); - } - - public static PresenceManager getInstance() { - return instance; - } - - private PresenceManager() { - subscriptionRequestProvider = new EntityNotificationProvider( - R.drawable.ic_stat_subscribe); - requestedSubscriptions = new HashMap>(); - presenceContainers = new NestedMap(); - readyAccounts = new ArrayList(); - } - - @Override - public void onLoad() { - Application.getInstance().runOnUiThread(new Runnable() { - @Override - public void run() { - onLoaded(); - } - }); - } - - private void onLoaded() { - NotificationManager.getInstance().registerNotificationProvider( - subscriptionRequestProvider); - } - - /** - * @param account - * @param user - * @return null can be returned. - */ - public SubscriptionRequest getSubscriptionRequest(String account, - String user) { - return subscriptionRequestProvider.get(account, user); - } - - /** - * Requests subscription to the contact. - * - * @param account - * @param bareAddress - * @throws NetworkException - */ - public void requestSubscription(String account, String bareAddress) - throws NetworkException { - Presence packet = new Presence(Presence.Type.subscribe); - packet.setTo(bareAddress); - ConnectionManager.getInstance().sendPacket(account, packet); - HashSet set = requestedSubscriptions.get(account); - if (set == null) { - set = new HashSet(); - requestedSubscriptions.put(account, set); - } - set.add(bareAddress); - } - - private void removeRequestedSubscription(String account, String bareAddress) { - HashSet set = requestedSubscriptions.get(account); - if (set != null) - set.remove(bareAddress); - } - - /** - * Accepts subscription request from the entity (share own presence). - * - * @param account - * @param bareAddress - * @throws NetworkException - */ - public void acceptSubscription(String account, String bareAddress) - throws NetworkException { - Presence packet = new Presence(Presence.Type.subscribed); - packet.setTo(bareAddress); - ConnectionManager.getInstance().sendPacket(account, packet); - subscriptionRequestProvider.remove(account, bareAddress); - removeRequestedSubscription(account, bareAddress); - } - - /** - * Discards subscription request from the entity (deny own presence - * sharing). - * - * @param account - * @param bareAddress - * @throws NetworkException - */ - public void discardSubscription(String account, String bareAddress) - throws NetworkException { - Presence packet = new Presence(Presence.Type.unsubscribed); - packet.setTo(bareAddress); - ConnectionManager.getInstance().sendPacket(account, packet); - subscriptionRequestProvider.remove(account, bareAddress); - removeRequestedSubscription(account, bareAddress); - } - - public boolean hasSubscriptionRequest(String account, String bareAddress) { - return getSubscriptionRequest(account, bareAddress) != null; - } - - /** - * @param account - * @param bareAddress - * @return Best resource item for specified user. null if there - * is no such user or user has no available resource. - */ - public ResourceItem getResourceItem(String account, String bareAddress) { - ResourceContainer resourceContainer = presenceContainers.get(account, - bareAddress); - if (resourceContainer == null) - return null; - return resourceContainer.getBest(); - } - - /** - * @return Collection with available resources. - */ - public Collection getResourceItems(String account, - String bareAddress) { - ResourceContainer container = presenceContainers.get(account, - bareAddress); - if (container == null) - return Collections.emptyList(); - return container.getResourceItems(); - } - - public StatusMode getStatusMode(String account, String bareAddress) { - ResourceItem resourceItem = getResourceItem(account, bareAddress); - if (resourceItem == null) - return StatusMode.unavailable; - return resourceItem.getStatusMode(); - } - - public String getStatusText(String account, String bareAddress) { - ResourceItem resourceItem = getResourceItem(account, bareAddress); - if (resourceItem == null) - return ""; - return resourceItem.getStatusText(); - } - - @Override - public void onPacket(ConnectionItem connection, String bareAddress, - Packet packet) { - if (!(connection instanceof AccountItem)) - return; - String account = ((AccountItem) connection).getAccount(); - if (packet instanceof Presence) { - if (bareAddress == null) - return; - Presence presence = (Presence) packet; - if (presence.getType() == Presence.Type.subscribe) { - // Subscription request - HashSet set = requestedSubscriptions.get(account); - if (set != null && set.contains(bareAddress)) { - try { - acceptSubscription(account, bareAddress); - } catch (NetworkException e) { - } - subscriptionRequestProvider.remove(account, bareAddress); - } else { - subscriptionRequestProvider.add(new SubscriptionRequest( - account, bareAddress), null); - } - return; - } - String verbose = StringUtils.parseResource(presence.getFrom()); - String resource = Jid.getResource(presence.getFrom()); - ResourceContainer resourceContainer = presenceContainers.get( - account, bareAddress); - ResourceItem resourceItem; - if (resourceContainer == null) - resourceItem = null; - else - resourceItem = resourceContainer.get(resource); - StatusMode previousStatusMode = getStatusMode(account, bareAddress); - String previousStatusText = getStatusText(account, bareAddress); - if (presence.getType() == Type.available) { - StatusMode statusMode = StatusMode.createStatusMode(presence); - String statusText = presence.getStatus(); - int priority = presence.getPriority(); - if (statusText == null) - statusText = ""; - if (priority == Integer.MIN_VALUE) - priority = 0; - if (resourceItem == null) { - if (resourceContainer == null) { - resourceContainer = new ResourceContainer(); - presenceContainers.put(account, bareAddress, - resourceContainer); - } - resourceContainer.put(resource, new ResourceItem(verbose, - statusMode, statusText, priority)); - resourceContainer.updateBest(); - } else { - resourceItem.setVerbose(verbose); - resourceItem.setStatusMode(statusMode); - resourceItem.setStatusText(statusText); - resourceItem.setPriority(priority); - resourceContainer.updateBest(); - } - } else if (presence.getType() == Presence.Type.error - || presence.getType() == Type.unavailable) { - if (presence.getType() == Presence.Type.error - && "".equals(resource) && resourceContainer != null) - presenceContainers.remove(account, bareAddress); - else if (resourceItem != null) { - resourceContainer.remove(resource); - resourceContainer.updateBest(); - } - } - - // Notify about changes - StatusMode newStatusMode = getStatusMode(account, bareAddress); - String newStatusText = getStatusText(account, bareAddress); - if (previousStatusMode != newStatusMode - || !previousStatusText.equals(newStatusText)) - for (OnStatusChangeListener listener : Application - .getInstance() - .getManagers(OnStatusChangeListener.class)) - if (previousStatusMode == newStatusMode) - listener.onStatusChanged(account, bareAddress, - resource, newStatusText); - else - listener.onStatusChanged(account, bareAddress, - resource, newStatusMode, newStatusText); - - RosterContact rosterContact = RosterManager.getInstance() - .getRosterContact(account, bareAddress); - if (rosterContact != null) { - ArrayList rosterContacts = new ArrayList(); - rosterContacts.add(rosterContact); - for (OnRosterChangedListener listener : Application - .getInstance().getManagers( - OnRosterChangedListener.class)) - listener.onPresenceChanged(rosterContacts); - } - - RosterManager.getInstance().onContactChanged(account, bareAddress); - } else if (packet instanceof RosterPacket - && ((RosterPacket) packet).getType() != IQ.Type.ERROR) { - RosterPacket rosterPacket = (RosterPacket) packet; - for (RosterPacket.Item item : rosterPacket.getRosterItems()) { - if (item.getItemType() == ItemType.both - || item.getItemType() == ItemType.from) { - String user = Jid.getBareAddress(item.getUser()); - if (user == null) - continue; - // Contact can be subscribed or unsubscribed from - // another IM. - subscriptionRequestProvider.remove(account, user); - } - } - } - } - - @Override - public void onArchiveModificationsReceived(ConnectionItem connection) { - if (!(connection instanceof AccountItem)) - return; - // Send presence information only when server side archive modifications - // received. - String account = ((AccountItem) connection).getAccount(); - readyAccounts.add(account); - Collection previous = new HashSet(); - for (NestedMap.Entry entry : presenceContainers) - previous.add(entry.getSecond()); - presenceContainers.clear(account); - ArrayList rosterContacts = new ArrayList(); - for (String bareAddress : previous) { - RosterContact rosterContact = RosterManager.getInstance() - .getRosterContact(account, bareAddress); - if (rosterContact != null) - rosterContacts.add(rosterContact); - } - for (OnRosterChangedListener listener : Application.getInstance() - .getManagers(OnRosterChangedListener.class)) - listener.onPresenceChanged(rosterContacts); - try { - resendPresence(account); - } catch (NetworkException e) { - } - } - - @Override - public void onDisconnect(ConnectionItem connection) { - if (!(connection instanceof AccountItem)) - return; - String account = ((AccountItem) connection).getAccount(); - readyAccounts.remove(account); - } - - @Override - public void onAccountDisabled(AccountItem accountItem) { - requestedSubscriptions.remove(accountItem.getAccount()); - presenceContainers.clear(accountItem.getAccount()); - } - - /** - * Sends new presence information. - * - * @param account - * @throws NetworkException - */ - public void resendPresence(String account) throws NetworkException { - if (!readyAccounts.contains(account)) - throw new NetworkException(R.string.NOT_CONNECTED); - ConnectionManager.getInstance().sendPacket(account, - AccountManager.getInstance().getAccount(account).getPresence()); - } + OnPacketListener, OnLoadListener, OnAccountDisabledListener, + OnDisconnectListener { + + private final static PresenceManager instance; + + static { + instance = new PresenceManager(); + Application.getInstance().addManager(instance); + } + + private final EntityNotificationProvider subscriptionRequestProvider; + /** + * List of account with requested subscriptions for auto accept incoming + * subscription request. + */ + private final HashMap> requestedSubscriptions; + /** + * Presence container for bare address in account. + */ + private final NestedMap presenceContainers; + /** + * Account ready to send / update its presence information. + */ + private final ArrayList readyAccounts; + + private PresenceManager() { + subscriptionRequestProvider = new EntityNotificationProvider( + R.drawable.ic_stat_add_circle); + requestedSubscriptions = new HashMap>(); + presenceContainers = new NestedMap(); + readyAccounts = new ArrayList(); + } + + public static PresenceManager getInstance() { + return instance; + } + + @Override + public void onLoad() { + Application.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + onLoaded(); + } + }); + } + + private void onLoaded() { + NotificationManager.getInstance().registerNotificationProvider( + subscriptionRequestProvider); + } + + /** + * @param account + * @param user + * @return null can be returned. + */ + public SubscriptionRequest getSubscriptionRequest(String account, + String user) { + return subscriptionRequestProvider.get(account, user); + } + + /** + * Requests subscription to the contact. + * + * @param account + * @param bareAddress + * @throws NetworkException + */ + public void requestSubscription(String account, String bareAddress) + throws NetworkException { + Presence packet = new Presence(Presence.Type.subscribe); + packet.setTo(bareAddress); + ConnectionManager.getInstance().sendPacket(account, packet); + HashSet set = requestedSubscriptions.get(account); + if (set == null) { + set = new HashSet(); + requestedSubscriptions.put(account, set); + } + set.add(bareAddress); + } + + private void removeRequestedSubscription(String account, String bareAddress) { + HashSet set = requestedSubscriptions.get(account); + if (set != null) + set.remove(bareAddress); + } + + /** + * Accepts subscription request from the entity (share own presence). + * + * @param account + * @param bareAddress + * @throws NetworkException + */ + public void acceptSubscription(String account, String bareAddress) + throws NetworkException { + Presence packet = new Presence(Presence.Type.subscribed); + packet.setTo(bareAddress); + ConnectionManager.getInstance().sendPacket(account, packet); + subscriptionRequestProvider.remove(account, bareAddress); + removeRequestedSubscription(account, bareAddress); + } + + /** + * Discards subscription request from the entity (deny own presence + * sharing). + * + * @param account + * @param bareAddress + * @throws NetworkException + */ + public void discardSubscription(String account, String bareAddress) + throws NetworkException { + Presence packet = new Presence(Presence.Type.unsubscribed); + packet.setTo(bareAddress); + ConnectionManager.getInstance().sendPacket(account, packet); + subscriptionRequestProvider.remove(account, bareAddress); + removeRequestedSubscription(account, bareAddress); + } + + public boolean hasSubscriptionRequest(String account, String bareAddress) { + return getSubscriptionRequest(account, bareAddress) != null; + } + + /** + * @param account + * @param bareAddress + * @return Best resource item for specified user. null if there + * is no such user or user has no available resource. + */ + public ResourceItem getResourceItem(String account, String bareAddress) { + ResourceContainer resourceContainer = presenceContainers.get(account, + bareAddress); + if (resourceContainer == null) + return null; + return resourceContainer.getBest(); + } + + /** + * @return Collection with available resources. + */ + public Collection getResourceItems(String account, + String bareAddress) { + ResourceContainer container = presenceContainers.get(account, + bareAddress); + if (container == null) + return Collections.emptyList(); + return container.getResourceItems(); + } + + public StatusMode getStatusMode(String account, String bareAddress) { + ResourceItem resourceItem = getResourceItem(account, bareAddress); + if (resourceItem == null) + return StatusMode.unavailable; + return resourceItem.getStatusMode(); + } + + public String getStatusText(String account, String bareAddress) { + ResourceItem resourceItem = getResourceItem(account, bareAddress); + if (resourceItem == null) + return ""; + return resourceItem.getStatusText(); + } + + @Override + public void onPacket(ConnectionItem connection, String bareAddress, + Packet packet) { + if (!(connection instanceof AccountItem)) + return; + String account = ((AccountItem) connection).getAccount(); + if (packet instanceof Presence) { + if (bareAddress == null) + return; + Presence presence = (Presence) packet; + if (presence.getType() == Presence.Type.subscribe) { + // Subscription request + HashSet set = requestedSubscriptions.get(account); + if (set != null && set.contains(bareAddress)) { + try { + acceptSubscription(account, bareAddress); + } catch (NetworkException e) { + } + subscriptionRequestProvider.remove(account, bareAddress); + } else { + subscriptionRequestProvider.add(new SubscriptionRequest( + account, bareAddress), null); + } + return; + } + String verbose = StringUtils.parseResource(presence.getFrom()); + String resource = Jid.getResource(presence.getFrom()); + ResourceContainer resourceContainer = presenceContainers.get( + account, bareAddress); + ResourceItem resourceItem; + if (resourceContainer == null) + resourceItem = null; + else + resourceItem = resourceContainer.get(resource); + StatusMode previousStatusMode = getStatusMode(account, bareAddress); + String previousStatusText = getStatusText(account, bareAddress); + if (presence.getType() == Type.available) { + StatusMode statusMode = StatusMode.createStatusMode(presence); + String statusText = presence.getStatus(); + int priority = presence.getPriority(); + if (statusText == null) + statusText = ""; + if (priority == Integer.MIN_VALUE) + priority = 0; + if (resourceItem == null) { + if (resourceContainer == null) { + resourceContainer = new ResourceContainer(); + presenceContainers.put(account, bareAddress, + resourceContainer); + } + resourceContainer.put(resource, new ResourceItem(verbose, + statusMode, statusText, priority)); + resourceContainer.updateBest(); + } else { + resourceItem.setVerbose(verbose); + resourceItem.setStatusMode(statusMode); + resourceItem.setStatusText(statusText); + resourceItem.setPriority(priority); + resourceContainer.updateBest(); + } + } else if (presence.getType() == Presence.Type.error + || presence.getType() == Type.unavailable) { + if (presence.getType() == Presence.Type.error + && "".equals(resource) && resourceContainer != null) + presenceContainers.remove(account, bareAddress); + else if (resourceItem != null) { + resourceContainer.remove(resource); + resourceContainer.updateBest(); + } + } + + // Notify about changes + StatusMode newStatusMode = getStatusMode(account, bareAddress); + String newStatusText = getStatusText(account, bareAddress); + if (previousStatusMode != newStatusMode + || !previousStatusText.equals(newStatusText)) + for (OnStatusChangeListener listener : Application + .getInstance() + .getManagers(OnStatusChangeListener.class)) + if (previousStatusMode == newStatusMode) + listener.onStatusChanged(account, bareAddress, + resource, newStatusText); + else + listener.onStatusChanged(account, bareAddress, + resource, newStatusMode, newStatusText); + + RosterContact rosterContact = RosterManager.getInstance() + .getRosterContact(account, bareAddress); + if (rosterContact != null) { + ArrayList rosterContacts = new ArrayList(); + rosterContacts.add(rosterContact); + for (OnRosterChangedListener listener : Application + .getInstance().getManagers( + OnRosterChangedListener.class)) + listener.onPresenceChanged(rosterContacts); + } + + RosterManager.getInstance().onContactChanged(account, bareAddress); + } else if (packet instanceof RosterPacket + && ((RosterPacket) packet).getType() != IQ.Type.ERROR) { + RosterPacket rosterPacket = (RosterPacket) packet; + for (RosterPacket.Item item : rosterPacket.getRosterItems()) { + if (item.getItemType() == ItemType.both + || item.getItemType() == ItemType.from) { + String user = Jid.getBareAddress(item.getUser()); + if (user == null) + continue; + // Contact can be subscribed or unsubscribed from + // another IM. + subscriptionRequestProvider.remove(account, user); + } + } + } + } + + @Override + public void onArchiveModificationsReceived(ConnectionItem connection) { + if (!(connection instanceof AccountItem)) + return; + // Send presence information only when server side archive modifications + // received. + String account = ((AccountItem) connection).getAccount(); + readyAccounts.add(account); + Collection previous = new HashSet(); + for (NestedMap.Entry entry : presenceContainers) + previous.add(entry.getSecond()); + presenceContainers.clear(account); + ArrayList rosterContacts = new ArrayList(); + for (String bareAddress : previous) { + RosterContact rosterContact = RosterManager.getInstance() + .getRosterContact(account, bareAddress); + if (rosterContact != null) + rosterContacts.add(rosterContact); + } + for (OnRosterChangedListener listener : Application.getInstance() + .getManagers(OnRosterChangedListener.class)) + listener.onPresenceChanged(rosterContacts); + try { + resendPresence(account); + } catch (NetworkException e) { + } + } + + @Override + public void onDisconnect(ConnectionItem connection) { + if (!(connection instanceof AccountItem)) + return; + String account = ((AccountItem) connection).getAccount(); + readyAccounts.remove(account); + } + + @Override + public void onAccountDisabled(AccountItem accountItem) { + requestedSubscriptions.remove(accountItem.getAccount()); + presenceContainers.clear(accountItem.getAccount()); + } + + /** + * Sends new presence information. + * + * @param account + * @throws NetworkException + */ + public void resendPresence(String account) throws NetworkException { + if (!readyAccounts.contains(account)) + throw new NetworkException(R.string.NOT_CONNECTED); + ConnectionManager.getInstance().sendPacket(account, + AccountManager.getInstance().getAccount(account).getPresence()); + } } diff --git a/app/src/main/java/com/xabber/android/data/roster/ResourceContainer.java b/app/src/main/java/com/xabber/android/data/roster/ResourceContainer.java index 4e40773a7b..e1aa769cb7 100644 --- a/app/src/main/java/com/xabber/android/data/roster/ResourceContainer.java +++ b/app/src/main/java/com/xabber/android/data/roster/ResourceContainer.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -22,89 +22,88 @@ /** * Contains information about resources for specified bare address. - * + * * @author alexander.ivanov - * */ class ResourceContainer { - /** - * List of available resources for its STRING-PREPed resources. - */ - private final Map resourceItems; + /** + * List of available resources for its STRING-PREPed resources. + */ + private final Map resourceItems; - /** - * Best resource. - */ - private ResourceItem best; + /** + * Best resource. + */ + private ResourceItem best; - public ResourceContainer() { - resourceItems = new HashMap(); - best = null; - } + public ResourceContainer() { + resourceItems = new HashMap(); + best = null; + } - /** - * @return Best resource according priority. null can be - * returned. - */ - ResourceItem getBest() { - return best; - } + /** + * @return Best resource according priority. null can be + * returned. + */ + ResourceItem getBest() { + return best; + } - /** - * Update information about best resource. - */ - void updateBest() { - String bestKey = null; - ResourceItem bestResource = null; - for (Entry entry : resourceItems.entrySet()) { - String key = entry.getKey(); - ResourceItem resource = entry.getValue(); - if (bestResource == null) { - bestKey = key; - bestResource = resource; - } else { - int result = resource.compareTo(bestResource); - if (result > 0 || (result == 0 && key.compareTo(bestKey) > 0)) - bestResource = resource; - } - } - this.best = bestResource; - } + /** + * Update information about best resource. + */ + void updateBest() { + String bestKey = null; + ResourceItem bestResource = null; + for (Entry entry : resourceItems.entrySet()) { + String key = entry.getKey(); + ResourceItem resource = entry.getValue(); + if (bestResource == null) { + bestKey = key; + bestResource = resource; + } else { + int result = resource.compareTo(bestResource); + if (result > 0 || (result == 0 && key.compareTo(bestKey) > 0)) + bestResource = resource; + } + } + this.best = bestResource; + } - /** - * @param resource - * @return Resource item for specified resource name. null can - * be returned. - */ - ResourceItem get(String resource) { - return resourceItems.get(resource); - } + /** + * @param resource + * @return Resource item for specified resource name. null can + * be returned. + */ + ResourceItem get(String resource) { + return resourceItems.get(resource); + } - /** - * Adds new resource item. - * - * @param resource - * @param resourceItem - */ - void put(String resource, ResourceItem resourceItem) { - resourceItems.put(resource, resourceItem); - } + /** + * Adds new resource item. + * + * @param resource + * @param resourceItem + */ + void put(String resource, ResourceItem resourceItem) { + resourceItems.put(resource, resourceItem); + } - /** - * Removes resource item. - * - * @param resource - */ - void remove(String resource) { - resourceItems.remove(resource); - } + /** + * Removes resource item. + * + * @param resource + */ + void remove(String resource) { + resourceItems.remove(resource); + } - /** - * @return Collection with available resources. - */ - Collection getResourceItems() { - return Collections.unmodifiableCollection(resourceItems.values()); - } + /** + * @return Collection with available resources. + */ + Collection getResourceItems() { + return Collections.unmodifiableCollection(resourceItems.values()); + } } diff --git a/app/src/main/java/com/xabber/android/data/roster/ResourceItem.java b/app/src/main/java/com/xabber/android/data/roster/ResourceItem.java index 6acc3a3725..2c8c7e8643 100644 --- a/app/src/main/java/com/xabber/android/data/roster/ResourceItem.java +++ b/app/src/main/java/com/xabber/android/data/roster/ResourceItem.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -19,94 +19,93 @@ /** * Represents information about contact's resource. - * + * * @author alexander.ivanov - * */ public class ResourceItem implements Comparable { - /** - * Unchanged resource name. - */ - private String verbose; - - private StatusMode statusMode; - private String statusText; - private int priority; - - public ResourceItem(String verbose, StatusMode statusMode, - String statusText, int priority) { - this.verbose = verbose; - this.statusMode = statusMode; - this.statusText = statusText; - this.priority = priority; - } - - /** - * Note: {@link Jid#getStringPrep(String)} before operate on it. - * - * @return Unchanged resource name. - */ - public String getVerbose() { - return verbose; - } - - public void setVerbose(String verbose) { - this.verbose = verbose; - } - - /** - * Note: {@link Jid#getStringPrep(String)} before operate on it. - * - * @param bareAddress - * @return Full JID. - */ - public String getUser(String bareAddress) { - return bareAddress + "/" + verbose; - } - - public StatusMode getStatusMode() { - return statusMode; - } - - public void setStatusMode(StatusMode statusMode) { - this.statusMode = statusMode; - } - - public String getStatusText() { - return statusText; - } - - public void setStatusText(String statusText) { - this.statusText = statusText; - } - - public int getPriority() { - return priority; - } - - public void setPriority(int priority) { - this.priority = priority; - } - - @Override - public int compareTo(ResourceItem another) { - int result = priority - another.priority; - if (result != 0) - return result; - return statusMode.compareTo(another.statusMode); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + priority; - result = prime * result - + ((statusMode == null) ? 0 : statusMode.hashCode()); - result = prime * result - + ((statusText == null) ? 0 : statusText.hashCode()); - result = prime * result + ((verbose == null) ? 0 : verbose.hashCode()); - return result; - } + /** + * Unchanged resource name. + */ + private String verbose; + + private StatusMode statusMode; + private String statusText; + private int priority; + + public ResourceItem(String verbose, StatusMode statusMode, + String statusText, int priority) { + this.verbose = verbose; + this.statusMode = statusMode; + this.statusText = statusText; + this.priority = priority; + } + + /** + * Note: {@link Jid#getStringPrep(String)} before operate on it. + * + * @return Unchanged resource name. + */ + public String getVerbose() { + return verbose; + } + + public void setVerbose(String verbose) { + this.verbose = verbose; + } + + /** + * Note: {@link Jid#getStringPrep(String)} before operate on it. + * + * @param bareAddress + * @return Full JID. + */ + public String getUser(String bareAddress) { + return bareAddress + "/" + verbose; + } + + public StatusMode getStatusMode() { + return statusMode; + } + + public void setStatusMode(StatusMode statusMode) { + this.statusMode = statusMode; + } + + public String getStatusText() { + return statusText; + } + + public void setStatusText(String statusText) { + this.statusText = statusText; + } + + public int getPriority() { + return priority; + } + + public void setPriority(int priority) { + this.priority = priority; + } + + @Override + public int compareTo(ResourceItem another) { + int result = priority - another.priority; + if (result != 0) + return result; + return statusMode.compareTo(another.statusMode); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + priority; + result = prime * result + + ((statusMode == null) ? 0 : statusMode.hashCode()); + result = prime * result + + ((statusText == null) ? 0 : statusText.hashCode()); + result = prime * result + ((verbose == null) ? 0 : verbose.hashCode()); + return result; + } } diff --git a/app/src/main/java/com/xabber/android/data/roster/RosterContact.java b/app/src/main/java/com/xabber/android/data/roster/RosterContact.java index 0ae3cc0b9d..fb8d86f550 100644 --- a/app/src/main/java/com/xabber/android/data/roster/RosterContact.java +++ b/app/src/main/java/com/xabber/android/data/roster/RosterContact.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -23,195 +23,194 @@ /** * Contact in roster. - * + *

* {@link #getUser()} always will be bare jid. - * + * * @author alexander.ivanov - * */ public class RosterContact extends AbstractContact { - /** - * Contact's name. - */ - protected String name; - - /** - * Used groups with its names. - */ - protected final Map groupReferences; - - /** - * Whether there is subscription of type "both" or "to". - */ - protected boolean subscribed; - - /** - * Whether contact`s account is connected. - */ - protected boolean connected; - - /** - * Whether contact`s account is enabled. - */ - protected boolean enabled; - - /** - * Raw contact id in system contact list. - */ - private Long rawId; - - /** - * Jid id in system contact list. - */ - private Long jidId; - - /** - * Nick name id in system contact list. - */ - private Long nickNameId; - - /** - * Structured name id in system contact list. - */ - private Long structuredNameId; - - /** - * Data id to view contact information from system contact list. - * - * Warning: not implemented yet. - */ - private Long viewId; - - public RosterContact(String account, String user, String name) { - super(account, user); - this.name = name; - groupReferences = new HashMap(); - subscribed = true; - connected = true; - enabled = true; - rawId = null; - jidId = null; - nickNameId = null; - structuredNameId = null; - viewId = null; - } - - /** - * @return real roster name value. - */ - public String getRealName() { - return name; - } - - void setName(String name) { - this.name = name; - } - - @Override - public Collection getGroups() { - return Collections.unmodifiableCollection(groupReferences.values()); - } - - Collection getGroupNames() { - return Collections.unmodifiableCollection(groupReferences.keySet()); - } - - /** - * @param groupName - * @return null or group with specified name. - */ - RosterGroupReference getRosterGroupReference(String groupName) { - return groupReferences.get(groupName); - } - - void addGroupReference(RosterGroupReference groupReference) { - groupReferences.put(groupReference.getName(), groupReference); - } - - void removeGroupReference(RosterGroupReference groupReference) { - groupReferences.remove(groupReference.getName()); - } - - void setSubscribed(boolean subscribed) { - this.subscribed = subscribed; - } - - @Override - public StatusMode getStatusMode() { - ResourceItem resourceItem = PresenceManager.getInstance() - .getResourceItem(account, user); - if (resourceItem == null) { - if (subscribed) - return StatusMode.unavailable; - else - return StatusMode.unsubscribed; - } - return resourceItem.getStatusMode(); - } - - @Override - public String getName() { - if (!"".equals(name)) - return name; - return super.getName(); - } - - @Override - public boolean isConnected() { - return connected; - } - - void setConnected(boolean connected) { - this.connected = connected; - } - - public boolean isEnabled() { - return enabled; - } - - void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - Long getRawId() { - return rawId; - } - - void setRawId(Long contactId) { - this.rawId = contactId; - } - - Long getJidId() { - return jidId; - } - - void setJidId(Long jidId) { - this.jidId = jidId; - } - - Long getNickNameId() { - return nickNameId; - } - - void setNickNameId(Long nameId) { - this.nickNameId = nameId; - } - - public Long getStructuredNameId() { - return structuredNameId; - } - - public void setStructuredNameId(Long structuredNameId) { - this.structuredNameId = structuredNameId; - } - - public Long getViewId() { - return viewId; - } - - public void setViewId(Long viewId) { - this.viewId = viewId; - } + /** + * Contact's name. + */ + protected String name; + + /** + * Used groups with its names. + */ + protected final Map groupReferences; + + /** + * Whether there is subscription of type "both" or "to". + */ + protected boolean subscribed; + + /** + * Whether contact`s account is connected. + */ + protected boolean connected; + + /** + * Whether contact`s account is enabled. + */ + protected boolean enabled; + + /** + * Raw contact id in system contact list. + */ + private Long rawId; + + /** + * Jid id in system contact list. + */ + private Long jidId; + + /** + * Nick name id in system contact list. + */ + private Long nickNameId; + + /** + * Structured name id in system contact list. + */ + private Long structuredNameId; + + /** + * Data id to view contact information from system contact list. + *

+ * Warning: not implemented yet. + */ + private Long viewId; + + public RosterContact(String account, String user, String name) { + super(account, user); + this.name = name; + groupReferences = new HashMap(); + subscribed = true; + connected = true; + enabled = true; + rawId = null; + jidId = null; + nickNameId = null; + structuredNameId = null; + viewId = null; + } + + /** + * @return real roster name value. + */ + public String getRealName() { + return name; + } + + void setName(String name) { + this.name = name; + } + + @Override + public Collection getGroups() { + return Collections.unmodifiableCollection(groupReferences.values()); + } + + Collection getGroupNames() { + return Collections.unmodifiableCollection(groupReferences.keySet()); + } + + /** + * @param groupName + * @return null or group with specified name. + */ + RosterGroupReference getRosterGroupReference(String groupName) { + return groupReferences.get(groupName); + } + + void addGroupReference(RosterGroupReference groupReference) { + groupReferences.put(groupReference.getName(), groupReference); + } + + void removeGroupReference(RosterGroupReference groupReference) { + groupReferences.remove(groupReference.getName()); + } + + void setSubscribed(boolean subscribed) { + this.subscribed = subscribed; + } + + @Override + public StatusMode getStatusMode() { + ResourceItem resourceItem = PresenceManager.getInstance() + .getResourceItem(account, user); + if (resourceItem == null) { + if (subscribed) + return StatusMode.unavailable; + else + return StatusMode.unsubscribed; + } + return resourceItem.getStatusMode(); + } + + @Override + public String getName() { + if (!"".equals(name)) + return name; + return super.getName(); + } + + @Override + public boolean isConnected() { + return connected; + } + + void setConnected(boolean connected) { + this.connected = connected; + } + + public boolean isEnabled() { + return enabled; + } + + void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + Long getRawId() { + return rawId; + } + + void setRawId(Long contactId) { + this.rawId = contactId; + } + + Long getJidId() { + return jidId; + } + + void setJidId(Long jidId) { + this.jidId = jidId; + } + + Long getNickNameId() { + return nickNameId; + } + + void setNickNameId(Long nameId) { + this.nickNameId = nameId; + } + + public Long getStructuredNameId() { + return structuredNameId; + } + + public void setStructuredNameId(Long structuredNameId) { + this.structuredNameId = structuredNameId; + } + + public Long getViewId() { + return viewId; + } + + public void setViewId(Long viewId) { + this.viewId = viewId; + } } diff --git a/app/src/main/java/com/xabber/android/data/roster/RosterGroup.java b/app/src/main/java/com/xabber/android/data/roster/RosterGroup.java index fa1df49246..22abe47402 100644 --- a/app/src/main/java/com/xabber/android/data/roster/RosterGroup.java +++ b/app/src/main/java/com/xabber/android/data/roster/RosterGroup.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,63 +18,62 @@ /** * Named roster group. - * + * * @author alexander.ivanov - * */ public class RosterGroup extends AccountRelated implements Group { - private final String name; + private final String name; - /** - * System contact list id. - * - * MUST BE MANAGED FROM BACKGROUND THREAD ONLY. - */ - private Long id; + /** + * System contact list id. + *

+ * MUST BE MANAGED FROM BACKGROUND THREAD ONLY. + */ + private Long id; - public RosterGroup(String account, String name) { - super(account); - this.name = name; - id = null; - } + public RosterGroup(String account, String name) { + super(account); + this.name = name; + id = null; + } - @Override - public String getName() { - return name; - } + @Override + public String getName() { + return name; + } - Long getId() { - return id; - } + Long getId() { + return id; + } - void setId(Long id) { - this.id = id; - } + void setId(Long id) { + this.id = id; + } - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((name == null) ? 0 : name.hashCode()); - return result; - } + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + ((name == null) ? 0 : name.hashCode()); + return result; + } - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (!super.equals(obj)) - return false; - if (getClass() != obj.getClass()) - return false; - RosterGroup other = (RosterGroup) obj; - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - return true; - } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + RosterGroup other = (RosterGroup) obj; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + return true; + } } diff --git a/app/src/main/java/com/xabber/android/data/roster/RosterGroupReference.java b/app/src/main/java/com/xabber/android/data/roster/RosterGroupReference.java index 5985763db3..ecfae67ff4 100644 --- a/app/src/main/java/com/xabber/android/data/roster/RosterGroupReference.java +++ b/app/src/main/java/com/xabber/android/data/roster/RosterGroupReference.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,44 +16,43 @@ /** * Named roster conctact's group. - * + * * @author alexander.ivanov - * */ public class RosterGroupReference implements Group { - private final RosterGroup rosterGroup; - - /** - * System contact list id. - * - * MUST BE MANAGED FROM BACKGROUND THREAD ONLY. - */ - private Long id; - - public RosterGroupReference(RosterGroup rosterGroup) { - this.rosterGroup = rosterGroup; - id = null; - } - - @Override - public String getName() { - return rosterGroup.getName(); - } - - /** - * @return the rosterGroup - */ - RosterGroup getRosterGroup() { - return rosterGroup; - } - - Long getId() { - return id; - } - - void setId(Long id) { - this.id = id; - } + private final RosterGroup rosterGroup; + + /** + * System contact list id. + *

+ * MUST BE MANAGED FROM BACKGROUND THREAD ONLY. + */ + private Long id; + + public RosterGroupReference(RosterGroup rosterGroup) { + this.rosterGroup = rosterGroup; + id = null; + } + + @Override + public String getName() { + return rosterGroup.getName(); + } + + /** + * @return the rosterGroup + */ + RosterGroup getRosterGroup() { + return rosterGroup; + } + + Long getId() { + return id; + } + + void setId(Long id) { + this.id = id; + } } diff --git a/app/src/main/java/com/xabber/android/data/roster/RosterManager.java b/app/src/main/java/com/xabber/android/data/roster/RosterManager.java index e105056aa6..121e64602a 100644 --- a/app/src/main/java/com/xabber/android/data/roster/RosterManager.java +++ b/app/src/main/java/com/xabber/android/data/roster/RosterManager.java @@ -1,32 +1,20 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data.roster; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smack.packet.Packet; -import org.jivesoftware.smack.packet.RosterPacket; -import org.jivesoftware.smack.packet.RosterPacket.ItemType; - +import com.xabber.android.R; import com.xabber.android.data.Application; import com.xabber.android.data.LogManager; import com.xabber.android.data.NetworkException; @@ -47,602 +35,615 @@ import com.xabber.android.data.message.AbstractChat; import com.xabber.android.data.message.ChatContact; import com.xabber.android.data.message.MessageManager; -import com.xabber.androiddev.R; import com.xabber.xmpp.address.Jid; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smack.packet.RosterPacket; +import org.jivesoftware.smack.packet.RosterPacket.ItemType; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + /** * Manage contact list (roster). - * + * * @author alexander.ivanov - * */ public class RosterManager implements OnDisconnectListener, OnPacketListener, - OnAccountEnabledListener, OnAccountDisabledListener, - OnArchiveModificationsReceivedListener, OnAccountRemovedListener { - - /** - * List of roster groups for its names in accounts. - */ - private final NestedMap rosterGroups; - - /** - * Managed contacts for bare addresses in accounts. - */ - private final NestedMap rosterContacts; - - /** - * List of accounts for witch roster was requested. - */ - private final Set requestedRosters; - - /** - * List of accounts for witch roster has been received. - */ - private final Set receivedRosters; - - private final static RosterManager instance; - - static { - instance = new RosterManager(); - Application.getInstance().addManager(instance); - } - - public static RosterManager getInstance() { - return instance; - } - - private RosterManager() { - rosterGroups = new NestedMap(); - rosterContacts = new NestedMap(); - receivedRosters = new HashSet(); - requestedRosters = new HashSet(); - } - - public Collection getContacts() { - return Collections.unmodifiableCollection(rosterContacts.values()); - } - - public Collection getRosterGroups() { - return Collections.unmodifiableCollection(rosterGroups.values()); - } - - /** - * @param account - * @param user - * @return null can be returned. - */ - public RosterContact getRosterContact(String account, String user) { - return rosterContacts.get(account, user); - } - - /** - * Gets {@link RoomContact}, {@link RosterContact}, {@link ChatContact} or - * creates new {@link ChatContact}. - * - * @param account - * @param user - * @return - */ - public AbstractContact getBestContact(String account, String user) { - AbstractChat abstractChat = MessageManager.getInstance().getChat( - account, user); - if (abstractChat != null && abstractChat instanceof RoomChat) - return new RoomContact((RoomChat) abstractChat); - RosterContact rosterContact = getRosterContact(account, user); - if (rosterContact != null) - return rosterContact; - if (abstractChat != null) - return new ChatContact(abstractChat); - return new ChatContact(account, user); - } - - /** - * Adds new group to be managed. - * - * @param contact - */ - void addRosterGroup(RosterGroup group) { - rosterGroups.put(group.getAccount(), group.getName(), group); - } - - /** - * Adds new contact to be managed. - * - * @param contact - */ - void addRosterContact(RosterContact contact) { - rosterContacts.put(contact.getAccount(), contact.getUser(), contact); - } - - /** - * Adds new contact to be managed and populates addedContacts map. - * - * @param contact - * @param name - * @param addedContacts - */ - private void addContact(RosterContact contact, String name, - Map addedContacts) { - addRosterContact(contact); - addedContacts.put(contact, name); - } - - /** - * Removes managed contact and populates removedContacts map. - * - * @param contact - * @param removedContacts - */ - private void removeContact(RosterContact contact, - Collection removedContacts) { - rosterContacts.remove(contact.getAccount(), contact.getUser()); - removedContacts.add(contact); - } - - /** - * Sets name for managed contact and populates renamedContacts map. - * - * @param contact - * @param name - * @param renamedContacts - */ - private void setName(RosterContact contact, String name, - Map renamedContacts) { - contact.setName(name); - renamedContacts.put(contact, name); - } - - /** - * Adds group to managed contact and populate addedGroups, - * addedGroupReference. - * - * @param contact - * @param groupName - * @param addedGroups - * @param addedGroupReference - */ - private void addGroup( - RosterContact contact, - String groupName, - Collection addedGroups, - Map> addedGroupReference) { - RosterGroup rosterGroup = rosterGroups.get(contact.getAccount(), - groupName); - if (rosterGroup == null) { - rosterGroup = new RosterGroup(contact.getAccount(), groupName); - addRosterGroup(rosterGroup); - addedGroups.add(rosterGroup); - } - RosterGroupReference groupReference = new RosterGroupReference( - rosterGroup); - contact.addGroupReference(groupReference); - Collection collection = addedGroupReference - .get(contact); - if (collection == null) { - collection = new ArrayList(); - addedGroupReference.put(contact, collection); - } - collection.add(groupReference); - } - - /** - * Removes group from managed contact and populates removedGroups, - * removedGroupReference. - * - * @param contact - * @param groupReference - * @param removedGroups - * @param removedGroupReference - */ - private void removeGroupReference( - RosterContact contact, - RosterGroupReference groupReference, - Collection removedGroups, - Map> removedGroupReference) { - contact.removeGroupReference(groupReference); - Collection collection = removedGroupReference - .get(contact); - if (collection == null) { - collection = new ArrayList(); - removedGroupReference.put(contact, collection); - } - collection.add(groupReference); - RosterGroup rosterGroup = groupReference.getRosterGroup(); - for (RosterContact check : rosterContacts.values()) - for (RosterGroupReference reference : check.getGroups()) - if (reference.getRosterGroup() == rosterGroup) - return; - rosterGroups.remove(rosterGroup.getAccount(), rosterGroup.getName()); - removedGroups.add(rosterGroup); - } - - /** - * @param account - * @return List of groups in specified account. - */ - public Collection getGroups(String account) { - return Collections.unmodifiableCollection(rosterGroups - .getNested(account).keySet()); - } - - /** - * @param account - * @param user - * @return Contact's name. - */ - public String getName(String account, String user) { - RosterContact contact = getRosterContact(account, user); - if (contact == null) - return user; - return contact.getName(); - } - - /** - * @param account - * @param user - * @return Contact's groups. - */ - public Collection getGroups(String account, String user) { - RosterContact contact = getRosterContact(account, user); - if (contact == null) - return Collections.emptyList(); - return contact.getGroupNames(); - } - - /** - * Requests to create new contact. - * - * @param account - * @param bareAddress - * @param name - * @param groups - * @throws NetworkException - */ - public void createContact(String account, String bareAddress, String name, - Collection groups) throws NetworkException { - RosterPacket packet = new RosterPacket(); - packet.setType(IQ.Type.SET); - RosterPacket.Item item = new RosterPacket.Item(bareAddress, name); - for (String group : groups) - if (group.trim().length() > 0) - item.addGroupName(group); - packet.addRosterItem(item); - ConnectionManager.getInstance().sendPacket(account, packet); - } - - /** - * Requests contact removing. - * - * @param account - * @param bareAddress - * @throws NetworkException - */ - public void removeContact(String account, String bareAddress) - throws NetworkException { - RosterPacket packet = new RosterPacket(); - packet.setType(IQ.Type.SET); - RosterPacket.Item item = new RosterPacket.Item(bareAddress, ""); - item.setItemType(RosterPacket.ItemType.remove); - packet.addRosterItem(item); - ConnectionManager.getInstance().sendPacket(account, packet); - } - - /** - * Requests to change contact's name and groups. - * - * @param account - * @param bareAddress - * @param name - * @param groups - * @throws NetworkException - */ - public void setNameAndGroup(String account, String bareAddress, - String name, Collection groups) throws NetworkException { - RosterContact contact = getRosterContact(account, bareAddress); - if (contact == null) - throw new NetworkException(R.string.ENTRY_IS_NOT_FOUND); - if (contact.getRealName().equals(name)) { - HashSet check = new HashSet(contact.getGroupNames()); - if (check.size() == groups.size()) { - check.removeAll(groups); - if (check.isEmpty()) - return; - } - } - RosterPacket packet = new RosterPacket(); - packet.setType(IQ.Type.SET); - RosterPacket.Item item = new RosterPacket.Item(bareAddress, name); - for (String group : groups) - item.addGroupName(group); - packet.addRosterItem(item); - ConnectionManager.getInstance().sendPacket(account, packet); - } - - /** - * Requests to remove group from all contacts in account. - * - * @param account - * @param group - * @throws NetworkException - */ - public void removeGroup(String account, String group) - throws NetworkException { - RosterPacket packet = new RosterPacket(); - packet.setType(IQ.Type.SET); - for (RosterContact contact : rosterContacts.getNested(account).values()) { - HashSet groups = new HashSet( - contact.getGroupNames()); - if (!groups.remove(group)) - continue; - RosterPacket.Item item = new RosterPacket.Item(contact.getUser(), - contact.getRealName()); - for (String one : groups) - item.addGroupName(one); - packet.addRosterItem(item); - } - if (packet.getRosterItemCount() == 0) - return; - ConnectionManager.getInstance().sendPacket(account, packet); - } - - /** - * Requests to remove group from all contacts in all accounts. - * - * @param group - * @throws NetworkException - */ - public void removeGroup(String group) throws NetworkException { - NetworkException networkException = null; - boolean success = false; - for (String account : AccountManager.getInstance().getAccounts()) { - try { - removeGroup(account, group); - } catch (NetworkException e) { - if (networkException == null) - networkException = e; - continue; - } - success = true; - } - if (!success && networkException != null) - throw networkException; - } - - /** - * Requests to rename group. - * - * @param account - * @param oldGroup - * can be null for "no group". - * @param newGroup - * @throws NetworkException - */ - public void renameGroup(String account, String oldGroup, String newGroup) - throws NetworkException { - if (newGroup.equals(oldGroup)) - return; - RosterPacket packet = new RosterPacket(); - packet.setType(IQ.Type.SET); - for (RosterContact contact : rosterContacts.getNested(account).values()) { - HashSet groups = new HashSet( - contact.getGroupNames()); - if (!groups.remove(oldGroup) - && !(oldGroup == null && groups.isEmpty())) - continue; - groups.add(newGroup); - RosterPacket.Item item = new RosterPacket.Item(contact.getUser(), - contact.getRealName()); - for (String one : groups) - item.addGroupName(one); - packet.addRosterItem(item); - } - if (packet.getRosterItemCount() == 0) - return; - ConnectionManager.getInstance().sendPacket(account, packet); - } - - /** - * Requests to rename group from all accounts. - * - * @param oldGroup - * can be null for "no group". - * @param newGroup - * @throws NetworkException - */ - public void renameGroup(String oldGroup, String newGroup) - throws NetworkException { - NetworkException networkException = null; - boolean success = false; - for (String account : AccountManager.getInstance().getAccounts()) { - try { - renameGroup(account, oldGroup, newGroup); - } catch (NetworkException e) { - if (networkException == null) - networkException = e; - continue; - } - success = true; - } - if (!success && networkException != null) - throw networkException; - } - - /** - * @param account - * @return Whether roster for specified account has been received. - */ - public boolean isRosterReceived(String account) { - return receivedRosters.contains(account); - } - - /** - * Sets whether contacts in accounts are enabled. - * - * @param account - * @param enabled - */ - private void setEnabled(String account, boolean enabled) { - for (RosterContact contact : rosterContacts.getNested(account).values()) - contact.setEnabled(enabled); - } - - @Override - public void onAccountEnabled(AccountItem accountItem) { - setEnabled(accountItem.getAccount(), true); - } - - @Override - public void onArchiveModificationsReceived(ConnectionItem connection) { - if (!(connection instanceof AccountItem)) - return; - // Request roster only when server side archive modifications - // received. - String account = ((AccountItem) connection).getAccount(); - requestedRosters.add(account); - try { - ConnectionManager.getInstance().sendPacket(account, - new RosterPacket()); - } catch (NetworkException e) { - LogManager.exception(this, e); - } - } - - @Override - public void onDisconnect(ConnectionItem connection) { - if (!(connection instanceof AccountItem)) - return; - String account = ((AccountItem) connection).getAccount(); - for (RosterContact contact : rosterContacts.getNested(account).values()) - contact.setConnected(false); - requestedRosters.remove(account); - receivedRosters.remove(account); - } - - @Override - public void onAccountDisabled(AccountItem accountItem) { - setEnabled(accountItem.getAccount(), false); - } - - @Override - public void onAccountRemoved(AccountItem accountItem) { - rosterGroups.clear(accountItem.getAccount()); - rosterContacts.clear(accountItem.getAccount()); - } - - @Override - public void onPacket(ConnectionItem connection, String bareAddress, - Packet packet) { - if (!(connection instanceof AccountItem)) - return; - String account = ((AccountItem) connection).getAccount(); - if (!(packet instanceof RosterPacket)) - return; - if (((RosterPacket) packet).getType() != IQ.Type.ERROR) { - boolean rosterWasReceived = requestedRosters.remove(account); - ArrayList remove = new ArrayList(); - if (rosterWasReceived) - for (RosterContact contact : rosterContacts.getNested(account) - .values()) { - contact.setConnected(true); - remove.add(contact); - } - RosterPacket rosterPacket = (RosterPacket) packet; - ArrayList entities = new ArrayList(); - Collection addedGroups = new ArrayList(); - Map addedContacts = new HashMap(); - Map renamedContacts = new HashMap(); - Map> addedGroupReference = new HashMap>(); - Map> removedGroupReference = new HashMap>(); - Collection removedContacts = new ArrayList(); - Collection removedGroups = new ArrayList(); - - for (RosterPacket.Item item : rosterPacket.getRosterItems()) { - String user = Jid.getBareAddress(item.getUser()); - if (user == null) - continue; - entities.add(new BaseEntity(account, user)); - RosterContact contact = getRosterContact(account, user); - if (item.getItemType() == RosterPacket.ItemType.remove) { - if (contact != null) - removeContact(contact, removedContacts); - } else { - String name = item.getName(); - if (name == null) - name = ""; - if (contact == null) { - contact = new RosterContact(account, user, name); - addContact(contact, name, addedContacts); - } else { - remove.remove(contact); - if (!contact.getRealName().equals(name)) - setName(contact, name, renamedContacts); - } - ArrayList removeGroupReferences = new ArrayList( - contact.getGroups()); - for (String groupName : item.getGroupNames()) { - RosterGroupReference rosterGroup = contact - .getRosterGroupReference(groupName); - if (rosterGroup == null) - addGroup(contact, groupName, addedGroups, - addedGroupReference); - else - removeGroupReferences.remove(rosterGroup); - } - for (RosterGroupReference rosterGroup : removeGroupReferences) - removeGroupReference(contact, rosterGroup, - removedGroups, removedGroupReference); - contact.setSubscribed(item.getItemType() == ItemType.both - || item.getItemType() == ItemType.to); - } - } - for (RosterContact contact : remove) { - entities.add(new BaseEntity(account, contact.getUser())); - removeContact(contact, removedContacts); - } - for (OnRosterChangedListener listener : Application.getInstance() - .getManagers(OnRosterChangedListener.class)) - listener.onRosterUpdate(addedGroups, addedContacts, - renamedContacts, addedGroupReference, - removedGroupReference, removedContacts, removedGroups); - onContactsChanged(entities); - if (rosterWasReceived) { - AccountItem accountItem = (AccountItem) connection; - receivedRosters.add(account); - for (OnRosterReceivedListener listener : Application - .getInstance().getManagers( - OnRosterReceivedListener.class)) - listener.onRosterReceived(accountItem); - AccountManager.getInstance().onAccountChanged(account); - } - } - } - - /** - * Notifies registered {@link OnContactChangedListener}. - * - * @param entities - */ - public void onContactsChanged(final Collection entities) { - Application.getInstance().runOnUiThread(new Runnable() { - @Override - public void run() { - for (OnContactChangedListener onContactChangedListener : Application - .getInstance().getUIListeners( - OnContactChangedListener.class)) - onContactChangedListener.onContactsChanged(entities); - } - }); - } - - /** - * Notifies registered {@link OnContactChangedListener}. - * - * @param entities - */ - public void onContactChanged(String account, String bareAddress) { - final ArrayList entities = new ArrayList(); - entities.add(new BaseEntity(account, bareAddress)); - onContactsChanged(entities); - } + OnAccountEnabledListener, OnAccountDisabledListener, + OnArchiveModificationsReceivedListener, OnAccountRemovedListener { + + private final static RosterManager instance; + + static { + instance = new RosterManager(); + Application.getInstance().addManager(instance); + } + + /** + * List of roster groups for its names in accounts. + */ + private final NestedMap rosterGroups; + /** + * Managed contacts for bare addresses in accounts. + */ + private final NestedMap rosterContacts; + /** + * List of accounts for witch roster was requested. + */ + private final Set requestedRosters; + /** + * List of accounts for witch roster has been received. + */ + private final Set receivedRosters; + + private RosterManager() { + rosterGroups = new NestedMap(); + rosterContacts = new NestedMap(); + receivedRosters = new HashSet(); + requestedRosters = new HashSet(); + } + + public static RosterManager getInstance() { + return instance; + } + + public Collection getContacts() { + return Collections.unmodifiableCollection(rosterContacts.values()); + } + + public Collection getRosterGroups() { + return Collections.unmodifiableCollection(rosterGroups.values()); + } + + /** + * @param account + * @param user + * @return null can be returned. + */ + public RosterContact getRosterContact(String account, String user) { + return rosterContacts.get(account, user); + } + + /** + * Gets {@link RoomContact}, {@link RosterContact}, {@link ChatContact} or + * creates new {@link ChatContact}. + * + * @param account + * @param user + * @return + */ + public AbstractContact getBestContact(String account, String user) { + AbstractChat abstractChat = MessageManager.getInstance().getChat( + account, user); + if (abstractChat != null && abstractChat instanceof RoomChat) + return new RoomContact((RoomChat) abstractChat); + RosterContact rosterContact = getRosterContact(account, user); + if (rosterContact != null) + return rosterContact; + if (abstractChat != null) + return new ChatContact(abstractChat); + return new ChatContact(account, user); + } + + /** + * Adds new group to be managed. + * + * @param contact + */ + void addRosterGroup(RosterGroup group) { + rosterGroups.put(group.getAccount(), group.getName(), group); + } + + /** + * Adds new contact to be managed. + * + * @param contact + */ + void addRosterContact(RosterContact contact) { + rosterContacts.put(contact.getAccount(), contact.getUser(), contact); + } + + /** + * Adds new contact to be managed and populates addedContacts map. + * + * @param contact + * @param name + * @param addedContacts + */ + private void addContact(RosterContact contact, String name, + Map addedContacts) { + addRosterContact(contact); + addedContacts.put(contact, name); + } + + /** + * Removes managed contact and populates removedContacts map. + * + * @param contact + * @param removedContacts + */ + private void removeContact(RosterContact contact, + Collection removedContacts) { + rosterContacts.remove(contact.getAccount(), contact.getUser()); + removedContacts.add(contact); + } + + /** + * Sets name for managed contact and populates renamedContacts map. + * + * @param contact + * @param name + * @param renamedContacts + */ + private void setName(RosterContact contact, String name, + Map renamedContacts) { + contact.setName(name); + renamedContacts.put(contact, name); + } + + /** + * Adds group to managed contact and populate addedGroups, + * addedGroupReference. + * + * @param contact + * @param groupName + * @param addedGroups + * @param addedGroupReference + */ + private void addGroup( + RosterContact contact, + String groupName, + Collection addedGroups, + Map> addedGroupReference) { + RosterGroup rosterGroup = rosterGroups.get(contact.getAccount(), + groupName); + if (rosterGroup == null) { + rosterGroup = new RosterGroup(contact.getAccount(), groupName); + addRosterGroup(rosterGroup); + addedGroups.add(rosterGroup); + } + RosterGroupReference groupReference = new RosterGroupReference( + rosterGroup); + contact.addGroupReference(groupReference); + Collection collection = addedGroupReference + .get(contact); + if (collection == null) { + collection = new ArrayList(); + addedGroupReference.put(contact, collection); + } + collection.add(groupReference); + } + + /** + * Removes group from managed contact and populates removedGroups, + * removedGroupReference. + * + * @param contact + * @param groupReference + * @param removedGroups + * @param removedGroupReference + */ + private void removeGroupReference( + RosterContact contact, + RosterGroupReference groupReference, + Collection removedGroups, + Map> removedGroupReference) { + contact.removeGroupReference(groupReference); + Collection collection = removedGroupReference + .get(contact); + if (collection == null) { + collection = new ArrayList(); + removedGroupReference.put(contact, collection); + } + collection.add(groupReference); + RosterGroup rosterGroup = groupReference.getRosterGroup(); + for (RosterContact check : rosterContacts.values()) + for (RosterGroupReference reference : check.getGroups()) + if (reference.getRosterGroup() == rosterGroup) + return; + rosterGroups.remove(rosterGroup.getAccount(), rosterGroup.getName()); + removedGroups.add(rosterGroup); + } + + /** + * @param account + * @return List of groups in specified account. + */ + public Collection getGroups(String account) { + return Collections.unmodifiableCollection(rosterGroups + .getNested(account).keySet()); + } + + /** + * @param account + * @param user + * @return Contact's name. + */ + public String getName(String account, String user) { + RosterContact contact = getRosterContact(account, user); + if (contact == null) + return user; + return contact.getName(); + } + + /** + * @param account + * @param user + * @return Contact's groups. + */ + public Collection getGroups(String account, String user) { + RosterContact contact = getRosterContact(account, user); + if (contact == null) + return Collections.emptyList(); + return contact.getGroupNames(); + } + + /** + * Requests to create new contact. + * + * @param account + * @param bareAddress + * @param name + * @param groups + * @throws NetworkException + */ + public void createContact(String account, String bareAddress, String name, + Collection groups) throws NetworkException { + RosterPacket packet = new RosterPacket(); + packet.setType(IQ.Type.SET); + RosterPacket.Item item = new RosterPacket.Item(bareAddress, name); + for (String group : groups) + if (group.trim().length() > 0) + item.addGroupName(group); + packet.addRosterItem(item); + ConnectionManager.getInstance().sendPacket(account, packet); + } + + /** + * Requests contact removing. + * + * @param account + * @param bareAddress + * @throws NetworkException + */ + public void removeContact(String account, String bareAddress) + throws NetworkException { + RosterPacket packet = new RosterPacket(); + packet.setType(IQ.Type.SET); + RosterPacket.Item item = new RosterPacket.Item(bareAddress, ""); + item.setItemType(RosterPacket.ItemType.remove); + packet.addRosterItem(item); + ConnectionManager.getInstance().sendPacket(account, packet); + } + + public void setGroups(String account, String bareAddress, Collection groups) throws NetworkException { + RosterContact contact = getRosterContact(account, bareAddress); + if (contact == null) { + throw new NetworkException(R.string.ENTRY_IS_NOT_FOUND); + } + + HashSet check = new HashSet<>(contact.getGroupNames()); + if (check.size() == groups.size()) { + check.removeAll(groups); + if (check.isEmpty()) + return; + } + + updateRosterContact(account, bareAddress, contact.getRealName(), groups); + } + + public void setName(String account, String bareAddress, String name) throws NetworkException { + RosterContact contact = getRosterContact(account, bareAddress); + if (contact == null) + throw new NetworkException(R.string.ENTRY_IS_NOT_FOUND); + if (contact.getRealName().equals(name)) { + return; + } + + updateRosterContact(account, bareAddress, name, contact.getGroupNames()); + } + + private void updateRosterContact(String account, String bareAddress, String name, Collection groups) throws NetworkException { + RosterPacket packet = new RosterPacket(); + packet.setType(IQ.Type.SET); + RosterPacket.Item item = new RosterPacket.Item(bareAddress, name); + for (String group : groups) { + item.addGroupName(group); + } + packet.addRosterItem(item); + ConnectionManager.getInstance().sendPacket(account, packet); + } + + /** + * Requests to remove group from all contacts in account. + * + * @param account + * @param group + * @throws NetworkException + */ + public void removeGroup(String account, String group) + throws NetworkException { + RosterPacket packet = new RosterPacket(); + packet.setType(IQ.Type.SET); + for (RosterContact contact : rosterContacts.getNested(account).values()) { + HashSet groups = new HashSet( + contact.getGroupNames()); + if (!groups.remove(group)) + continue; + RosterPacket.Item item = new RosterPacket.Item(contact.getUser(), + contact.getRealName()); + for (String one : groups) + item.addGroupName(one); + packet.addRosterItem(item); + } + if (packet.getRosterItemCount() == 0) + return; + ConnectionManager.getInstance().sendPacket(account, packet); + } + + /** + * Requests to remove group from all contacts in all accounts. + * + * @param group + * @throws NetworkException + */ + public void removeGroup(String group) throws NetworkException { + NetworkException networkException = null; + boolean success = false; + for (String account : AccountManager.getInstance().getAccounts()) { + try { + removeGroup(account, group); + } catch (NetworkException e) { + if (networkException == null) + networkException = e; + continue; + } + success = true; + } + if (!success && networkException != null) + throw networkException; + } + + /** + * Requests to rename group. + * + * @param account + * @param oldGroup can be null for "no group". + * @param newGroup + * @throws NetworkException + */ + public void renameGroup(String account, String oldGroup, String newGroup) + throws NetworkException { + if (newGroup.equals(oldGroup)) + return; + RosterPacket packet = new RosterPacket(); + packet.setType(IQ.Type.SET); + for (RosterContact contact : rosterContacts.getNested(account).values()) { + HashSet groups = new HashSet( + contact.getGroupNames()); + if (!groups.remove(oldGroup) + && !(oldGroup == null && groups.isEmpty())) + continue; + groups.add(newGroup); + RosterPacket.Item item = new RosterPacket.Item(contact.getUser(), + contact.getRealName()); + for (String one : groups) + item.addGroupName(one); + packet.addRosterItem(item); + } + if (packet.getRosterItemCount() == 0) + return; + ConnectionManager.getInstance().sendPacket(account, packet); + } + + /** + * Requests to rename group from all accounts. + * + * @param oldGroup can be null for "no group". + * @param newGroup + * @throws NetworkException + */ + public void renameGroup(String oldGroup, String newGroup) + throws NetworkException { + NetworkException networkException = null; + boolean success = false; + for (String account : AccountManager.getInstance().getAccounts()) { + try { + renameGroup(account, oldGroup, newGroup); + } catch (NetworkException e) { + if (networkException == null) + networkException = e; + continue; + } + success = true; + } + if (!success && networkException != null) + throw networkException; + } + + /** + * @param account + * @return Whether roster for specified account has been received. + */ + public boolean isRosterReceived(String account) { + return receivedRosters.contains(account); + } + + /** + * Sets whether contacts in accounts are enabled. + * + * @param account + * @param enabled + */ + private void setEnabled(String account, boolean enabled) { + for (RosterContact contact : rosterContacts.getNested(account).values()) + contact.setEnabled(enabled); + } + + @Override + public void onAccountEnabled(AccountItem accountItem) { + setEnabled(accountItem.getAccount(), true); + } + + @Override + public void onArchiveModificationsReceived(ConnectionItem connection) { + if (!(connection instanceof AccountItem)) + return; + // Request roster only when server side archive modifications + // received. + String account = ((AccountItem) connection).getAccount(); + requestedRosters.add(account); + try { + ConnectionManager.getInstance().sendPacket(account, + new RosterPacket()); + } catch (NetworkException e) { + LogManager.exception(this, e); + } + } + + @Override + public void onDisconnect(ConnectionItem connection) { + if (!(connection instanceof AccountItem)) + return; + String account = ((AccountItem) connection).getAccount(); + for (RosterContact contact : rosterContacts.getNested(account).values()) + contact.setConnected(false); + requestedRosters.remove(account); + receivedRosters.remove(account); + } + + @Override + public void onAccountDisabled(AccountItem accountItem) { + setEnabled(accountItem.getAccount(), false); + } + + @Override + public void onAccountRemoved(AccountItem accountItem) { + rosterGroups.clear(accountItem.getAccount()); + rosterContacts.clear(accountItem.getAccount()); + } + + @Override + public void onPacket(ConnectionItem connection, String bareAddress, + Packet packet) { + if (!(connection instanceof AccountItem)) + return; + String account = ((AccountItem) connection).getAccount(); + if (!(packet instanceof RosterPacket)) + return; + if (((RosterPacket) packet).getType() != IQ.Type.ERROR) { + boolean rosterWasReceived = requestedRosters.remove(account); + ArrayList remove = new ArrayList(); + if (rosterWasReceived) + for (RosterContact contact : rosterContacts.getNested(account) + .values()) { + contact.setConnected(true); + remove.add(contact); + } + RosterPacket rosterPacket = (RosterPacket) packet; + ArrayList entities = new ArrayList(); + Collection addedGroups = new ArrayList(); + Map addedContacts = new HashMap(); + Map renamedContacts = new HashMap(); + Map> addedGroupReference = new HashMap>(); + Map> removedGroupReference = new HashMap>(); + Collection removedContacts = new ArrayList(); + Collection removedGroups = new ArrayList(); + + for (RosterPacket.Item item : rosterPacket.getRosterItems()) { + String user = Jid.getBareAddress(item.getUser()); + if (user == null) + continue; + entities.add(new BaseEntity(account, user)); + RosterContact contact = getRosterContact(account, user); + if (item.getItemType() == RosterPacket.ItemType.remove) { + if (contact != null) + removeContact(contact, removedContacts); + } else { + String name = item.getName(); + if (name == null) + name = ""; + if (contact == null) { + contact = new RosterContact(account, user, name); + addContact(contact, name, addedContacts); + } else { + remove.remove(contact); + if (!contact.getRealName().equals(name)) + setName(contact, name, renamedContacts); + } + ArrayList removeGroupReferences = new ArrayList( + contact.getGroups()); + for (String groupName : item.getGroupNames()) { + RosterGroupReference rosterGroup = contact + .getRosterGroupReference(groupName); + if (rosterGroup == null) + addGroup(contact, groupName, addedGroups, + addedGroupReference); + else + removeGroupReferences.remove(rosterGroup); + } + for (RosterGroupReference rosterGroup : removeGroupReferences) + removeGroupReference(contact, rosterGroup, + removedGroups, removedGroupReference); + contact.setSubscribed(item.getItemType() == ItemType.both + || item.getItemType() == ItemType.to); + } + } + for (RosterContact contact : remove) { + entities.add(new BaseEntity(account, contact.getUser())); + removeContact(contact, removedContacts); + } + for (OnRosterChangedListener listener : Application.getInstance() + .getManagers(OnRosterChangedListener.class)) + listener.onRosterUpdate(addedGroups, addedContacts, + renamedContacts, addedGroupReference, + removedGroupReference, removedContacts, removedGroups); + onContactsChanged(entities); + if (rosterWasReceived) { + AccountItem accountItem = (AccountItem) connection; + receivedRosters.add(account); + for (OnRosterReceivedListener listener : Application + .getInstance().getManagers( + OnRosterReceivedListener.class)) + listener.onRosterReceived(accountItem); + AccountManager.getInstance().onAccountChanged(account); + } + } + } + + /** + * Notifies registered {@link OnContactChangedListener}. + * + * @param entities + */ + public void onContactsChanged(final Collection entities) { + Application.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + for (OnContactChangedListener onContactChangedListener : Application + .getInstance().getUIListeners( + OnContactChangedListener.class)) + onContactChangedListener.onContactsChanged(entities); + } + }); + } + + /** + * Notifies registered {@link OnContactChangedListener}. + * + * @param entities + */ + public void onContactChanged(String account, String bareAddress) { + final ArrayList entities = new ArrayList(); + entities.add(new BaseEntity(account, bareAddress)); + onContactsChanged(entities); + } } diff --git a/app/src/main/java/com/xabber/android/data/roster/ShowOfflineMode.java b/app/src/main/java/com/xabber/android/data/roster/ShowOfflineMode.java index a7d64aa8d4..c82c615967 100644 --- a/app/src/main/java/com/xabber/android/data/roster/ShowOfflineMode.java +++ b/app/src/main/java/com/xabber/android/data/roster/ShowOfflineMode.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,25 +16,23 @@ /** * Mode of showing offline contacts. - * + * * @author alexander.ivanov - * */ public enum ShowOfflineMode { - /** - * Never show offline contacts. - */ - never, + /** + * Always show offline contacts. + */ + always, - /** - * Show offline contacts according to global settings. - */ - normal, - - /** - * Always show offline contacts. - */ - always; + /** + * Show offline contacts according to global settings. + */ + normal, + /** + * Never show offline contacts. + */ + never } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/data/roster/StructuredName.java b/app/src/main/java/com/xabber/android/data/roster/StructuredName.java index 434f85171b..6c5db27008 100644 --- a/app/src/main/java/com/xabber/android/data/roster/StructuredName.java +++ b/app/src/main/java/com/xabber/android/data/roster/StructuredName.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,75 +16,74 @@ /** * vCard-based structured name. - * + * * @author alexander.ivanov - * */ public class StructuredName { - private final String nickName; - private final String formattedName; - private final String firstName; - private final String middleName; - private final String lastName; - private final String bestName; + private final String nickName; + private final String formattedName; + private final String firstName; + private final String middleName; + private final String lastName; + private final String bestName; - public StructuredName(String nickName, String formattedName, - String firstName, String middleName, String lastName) { - super(); - this.nickName = nickName == null ? "" : nickName; - this.formattedName = formattedName == null ? "" : formattedName; - this.firstName = firstName == null ? "" : firstName; - this.middleName = middleName == null ? "" : middleName; - this.lastName = lastName == null ? "" : lastName; - if (!"".equals(this.nickName)) - bestName = this.nickName; - else if (!"".equals(this.formattedName)) - bestName = this.formattedName; - else - bestName = ""; - } + public StructuredName(String nickName, String formattedName, + String firstName, String middleName, String lastName) { + super(); + this.nickName = nickName == null ? "" : nickName; + this.formattedName = formattedName == null ? "" : formattedName; + this.firstName = firstName == null ? "" : firstName; + this.middleName = middleName == null ? "" : middleName; + this.lastName = lastName == null ? "" : lastName; + if (!"".equals(this.nickName)) + bestName = this.nickName; + else if (!"".equals(this.formattedName)) + bestName = this.formattedName; + else + bestName = ""; + } - /** - * @return the nick name. - */ - public String getNickName() { - return nickName; - } + /** + * @return the nick name. + */ + public String getNickName() { + return nickName; + } - /** - * @return the formatted name. - */ - public String getFormattedName() { - return formattedName; - } + /** + * @return the formatted name. + */ + public String getFormattedName() { + return formattedName; + } - /** - * @return the first name. - */ - public String getFirstName() { - return firstName; - } + /** + * @return the first name. + */ + public String getFirstName() { + return firstName; + } - /** - * @return the middle name. - */ - public String getMiddleName() { - return middleName; - } + /** + * @return the middle name. + */ + public String getMiddleName() { + return middleName; + } - /** - * @return the last name. - */ - public String getLastName() { - return lastName; - } + /** + * @return the last name. + */ + public String getLastName() { + return lastName; + } - /** - * @return the nick name or formatted name. - */ - public String getBestName() { - return bestName; - } + /** + * @return the nick name or formatted name. + */ + public String getBestName() { + return bestName; + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/data/roster/SubscriptionRequest.java b/app/src/main/java/com/xabber/android/data/roster/SubscriptionRequest.java index aa35c67054..27872cf828 100644 --- a/app/src/main/java/com/xabber/android/data/roster/SubscriptionRequest.java +++ b/app/src/main/java/com/xabber/android/data/roster/SubscriptionRequest.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,43 +16,38 @@ import android.content.Intent; +import com.xabber.android.R; import com.xabber.android.data.Application; import com.xabber.android.data.account.AccountManager; import com.xabber.android.data.entity.BaseEntity; import com.xabber.android.data.notification.EntityNotificationItem; -import com.xabber.android.ui.ContactAdd; -import com.xabber.androiddev.R; - -public class SubscriptionRequest extends BaseEntity implements - EntityNotificationItem { - - public SubscriptionRequest(String account, String user) { - super(account, user); - } - - @Override - public Intent getIntent() { - return ContactAdd.createSubscriptionIntent(Application.getInstance(), - account, user); - } - - @Override - public String getText() { - return Application.getInstance().getString( - R.string.subscription_request_message); - } - - @Override - public String getTitle() { - return user; - } - - public String getConfirmation() { - String accountName = AccountManager.getInstance().getVerboseName( - account); - String userName = RosterManager.getInstance().getName(account, user); - return Application.getInstance().getString( - R.string.contact_subscribe_confirm, userName, accountName); - } +import com.xabber.android.ui.ContactSubscription; + +public class SubscriptionRequest extends BaseEntity implements EntityNotificationItem { + + public SubscriptionRequest(String account, String user) { + super(account, user); + } + + @Override + public Intent getIntent() { + return ContactSubscription.createIntent(Application.getInstance(), account, user); + } + + @Override + public String getText() { + return Application.getInstance().getString( + R.string.subscription_request_message); + } + + @Override + public String getTitle() { + return user; + } + + public String getConfirmation() { + String accountName = AccountManager.getInstance().getVerboseName(account); + return Application.getInstance().getString(R.string.contact_subscribe_confirm, accountName); + } } diff --git a/app/src/main/java/com/xabber/android/data/roster/SyncManager.java b/app/src/main/java/com/xabber/android/data/roster/SyncManager.java index 3849f4a13a..40a6e69793 100644 --- a/app/src/main/java/com/xabber/android/data/roster/SyncManager.java +++ b/app/src/main/java/com/xabber/android/data/roster/SyncManager.java @@ -1,28 +1,19 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.data.roster; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.OnAccountsUpdateListener; @@ -49,6 +40,7 @@ import android.provider.ContactsContract.RawContacts; import android.provider.ContactsContract.StatusUpdates; +import com.xabber.android.R; import com.xabber.android.data.Application; import com.xabber.android.data.DatabaseManager; import com.xabber.android.data.LogManager; @@ -62,1170 +54,1165 @@ import com.xabber.android.data.entity.BaseEntity; import com.xabber.android.data.extension.vcard.VCardManager; import com.xabber.android.utils.DummyCursor; -import com.xabber.androiddev.R; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; /** * Manage integration with system accounts and contacts. - * + *

* All operation and states are managed from background thread. - * + * * @author alexander.ivanov - * * @see {@link Application#isContactsSupported()}. - * */ @SuppressLint("UseSparseArrays") @TargetApi(5) public class SyncManager implements OnLoadListener, OnUnloadListener, - OnAccountAddedListener, OnAccountRemovedListener, - OnAccountSyncableChangedListener, OnAccountsUpdateListener, - OnRosterChangedListener { - - private static boolean LOG = true; - - private static final Uri RAW_CONTACTS_URI = RawContacts.CONTENT_URI - .buildUpon() - .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, - "true").build(); - - private static final Uri GROUPS_URI = Groups.CONTENT_URI - .buildUpon() - .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, - "true").build(); - - private static final Uri DATA_URI = Data.CONTENT_URI - .buildUpon() - .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, - "true").build(); - - private final Application application; - - /** - * List of contacts with specified status. - */ - private final HashMap statuses; - - /** - * System account manager. - */ - private final AccountManager accountManager; - - /** - * Whether system accounts must be created on xabber account add. - * - * Used to prevent system account creation on load. - */ - private boolean createAccounts; - - /** - * Whether OnAccountsUpdatedListener was registered. - */ - private boolean registeredOnAccountsUpdatedListener; + OnAccountAddedListener, OnAccountRemovedListener, + OnAccountSyncableChangedListener, OnAccountsUpdateListener, + OnRosterChangedListener { - /** - * Accounts which contacts is indented to be synchronized. - */ - private final HashSet syncableAccounts; + private static final Uri RAW_CONTACTS_URI = RawContacts.CONTENT_URI + .buildUpon() + .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, + "true").build(); + private static final Uri GROUPS_URI = Groups.CONTENT_URI + .buildUpon() + .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, + "true").build(); + private static final Uri DATA_URI = Data.CONTENT_URI + .buildUpon() + .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, + "true").build(); + private final static SyncManager instance; + private static boolean LOG = true; - private final static SyncManager instance; + static { + instance = new SyncManager(); + Application.getInstance().addManager(instance); + } - static { - instance = new SyncManager(); - Application.getInstance().addManager(instance); - } + private final Application application; + /** + * List of contacts with specified status. + */ + private final HashMap statuses; + /** + * System account manager. + */ + private final AccountManager accountManager; + /** + * Accounts which contacts is indented to be synchronized. + */ + private final HashSet syncableAccounts; + /** + * Whether system accounts must be created on xabber account add. + *

+ * Used to prevent system account creation on load. + */ + private boolean createAccounts; + /** + * Whether OnAccountsUpdatedListener was registered. + */ + private boolean registeredOnAccountsUpdatedListener; - public static SyncManager getInstance() { - return instance; - } + private SyncManager() { + this.application = Application.getInstance(); + statuses = new HashMap(); + syncableAccounts = new HashSet(); + accountManager = AccountManager.get(application); + createAccounts = false; + registeredOnAccountsUpdatedListener = false; + } - private SyncManager() { - this.application = Application.getInstance(); - statuses = new HashMap(); - syncableAccounts = new HashSet(); - accountManager = AccountManager.get(application); - createAccounts = false; - registeredOnAccountsUpdatedListener = false; - } + public static SyncManager getInstance() { + return instance; + } - /** - * @return Account type used by system contact list. - */ - public String getAccountType() { - return application.getString(R.string.sync_account_type); - } + /** + * @return Account type used by system contact list. + */ + public String getAccountType() { + return application.getString(R.string.sync_account_type); + } - /** - * Returns first entry form the map. Populate otherIds if map contacts more - * then one entry. - * - * @param map - * can be null. - * @param otherIds - * collection of keys for not first entries. - * @return null if map is null or map has no - * elements. - */ - private Entry getFirstEntry(HashMap map, - Collection otherIds) { - if (map == null) - return null; - Entry result = null; - for (Entry entry : map.entrySet()) - if (result == null) - result = entry; - else { - LogManager.w(this, "Remove data: " + entry.getKey() + ": " - + entry.getValue()); - otherIds.add(entry.getKey()); - } - return result; - } + /** + * Returns first entry form the map. Populate otherIds if map contacts more + * then one entry. + * + * @param map can be null. + * @param otherIds collection of keys for not first entries. + * @return null if map is null or map has no + * elements. + */ + private Entry getFirstEntry(HashMap map, + Collection otherIds) { + if (map == null) + return null; + Entry result = null; + for (Entry entry : map.entrySet()) + if (result == null) + result = entry; + else { + LogManager.w(this, "Remove data: " + entry.getKey() + ": " + + entry.getValue()); + otherIds.add(entry.getKey()); + } + return result; + } - /** - * Creates dummy cursor if passed cursor is null. - * - * @param cursor - * @return - */ - private Cursor checkCursor(Cursor cursor) { - if (cursor == null) - return new DummyCursor(); - return cursor; - } + /** + * Creates dummy cursor if passed cursor is null. + * + * @param cursor + * @return + */ + private Cursor checkCursor(Cursor cursor) { + if (cursor == null) + return new DummyCursor(); + return cursor; + } - @Override - public void onLoad() { - Cursor cursor; + @Override + public void onLoad() { + Cursor cursor; - // List of ids to be removed - ArrayList removeGroupIds = new ArrayList(); - ArrayList removeRawIds = new ArrayList(); - ArrayList removeDataIds = new ArrayList(); + // List of ids to be removed + ArrayList removeGroupIds = new ArrayList(); + ArrayList removeRawIds = new ArrayList(); + ArrayList removeDataIds = new ArrayList(); - // Load groups - HashMap groupsForGroupIds = new HashMap(); - cursor = application.getContentResolver().query(GROUPS_URI, - new String[] { Groups._ID, Groups.ACCOUNT_NAME, Groups.TITLE }, - Groups.ACCOUNT_TYPE + " = ?", - new String[] { getAccountType() }, null); - cursor = checkCursor(cursor); - try { - int idIndex = cursor.getColumnIndex(Groups._ID); - int accountIndex = cursor.getColumnIndex(Groups.ACCOUNT_NAME); - int titleIndex = cursor.getColumnIndex(Groups.TITLE); - while (cursor.moveToNext()) { - long groupId = cursor.getLong(idIndex); - String account = cursor.getString(accountIndex); - String name = cursor.getString(titleIndex); - RosterGroup rosterGroup = new RosterGroup(account, name); - rosterGroup.setId(groupId); - groupsForGroupIds.put(groupId, rosterGroup); - } - } finally { - try { - cursor.close(); - } catch (Exception e) { - LogManager.exception(this, e); - } - } - if (LOG) - LogManager.i(this, "Groups: " + groupsForGroupIds.size()); + // Load groups + HashMap groupsForGroupIds = new HashMap(); + cursor = application.getContentResolver().query(GROUPS_URI, + new String[]{Groups._ID, Groups.ACCOUNT_NAME, Groups.TITLE}, + Groups.ACCOUNT_TYPE + " = ?", + new String[]{getAccountType()}, null); + cursor = checkCursor(cursor); + try { + int idIndex = cursor.getColumnIndex(Groups._ID); + int accountIndex = cursor.getColumnIndex(Groups.ACCOUNT_NAME); + int titleIndex = cursor.getColumnIndex(Groups.TITLE); + while (cursor.moveToNext()) { + long groupId = cursor.getLong(idIndex); + String account = cursor.getString(accountIndex); + String name = cursor.getString(titleIndex); + RosterGroup rosterGroup = new RosterGroup(account, name); + rosterGroup.setId(groupId); + groupsForGroupIds.put(groupId, rosterGroup); + } + } finally { + try { + cursor.close(); + } catch (Exception e) { + LogManager.exception(this, e); + } + } + if (LOG) + LogManager.i(this, "Groups: " + groupsForGroupIds.size()); - // Load raw contacts with its accounts - HashMap accountsForRawIds = new HashMap(); - cursor = application.getContentResolver().query(RAW_CONTACTS_URI, - new String[] { RawContacts._ID, RawContacts.ACCOUNT_NAME }, - RawContacts.ACCOUNT_TYPE + " = ?", - new String[] { getAccountType() }, null); - cursor = checkCursor(cursor); - try { - int idIndex = cursor.getColumnIndex(RawContacts._ID); - int accountIndex = cursor.getColumnIndex(RawContacts.ACCOUNT_NAME); - while (cursor.moveToNext()) { - long id = cursor.getLong(idIndex); - String account = cursor.getString(accountIndex); - accountsForRawIds.put(id, account); - } - } finally { - try { - cursor.close(); - } catch (Exception e) { - LogManager.exception(this, e); - } - } - if (LOG) - LogManager.i(this, "Raw contacts: " + accountsForRawIds.size()); + // Load raw contacts with its accounts + HashMap accountsForRawIds = new HashMap(); + cursor = application.getContentResolver().query(RAW_CONTACTS_URI, + new String[]{RawContacts._ID, RawContacts.ACCOUNT_NAME}, + RawContacts.ACCOUNT_TYPE + " = ?", + new String[]{getAccountType()}, null); + cursor = checkCursor(cursor); + try { + int idIndex = cursor.getColumnIndex(RawContacts._ID); + int accountIndex = cursor.getColumnIndex(RawContacts.ACCOUNT_NAME); + while (cursor.moveToNext()) { + long id = cursor.getLong(idIndex); + String account = cursor.getString(accountIndex); + accountsForRawIds.put(id, account); + } + } finally { + try { + cursor.close(); + } catch (Exception e) { + LogManager.exception(this, e); + } + } + if (LOG) + LogManager.i(this, "Raw contacts: " + accountsForRawIds.size()); - // Load data - HashMap> jidsForDataIdForRawIds = new HashMap>(); - HashMap> emailsForDataIdForRawIds = new HashMap>(); - HashMap> namesForDataIdForRawIds = new HashMap>(); - HashMap> groupsForDataIdForRawIds = new HashMap>(); - HashMap structuredForRawIds = new HashMap(); - if (accountsForRawIds.isEmpty()) - cursor = null; - else - cursor = application.getContentResolver().query( - DATA_URI, - new String[] { Data._ID, Data.MIMETYPE, - Data.RAW_CONTACT_ID, Data.DATA1, }, - Data.MIMETYPE + " IN ( ?, ?, ?, ?, ?, ? )", - new String[] { Im.CONTENT_ITEM_TYPE, - Email.CONTENT_ITEM_TYPE, - Nickname.CONTENT_ITEM_TYPE, - GroupMembership.CONTENT_ITEM_TYPE, - CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE }, - null); - cursor = checkCursor(cursor); - try { - int idIndex = cursor.getColumnIndex(Data._ID); - int mimeTypeIndex = cursor.getColumnIndex(Data.MIMETYPE); - int rawIndex = cursor.getColumnIndex(Data.RAW_CONTACT_ID); - int dataIndex = cursor.getColumnIndex(Im.DATA); - while (cursor.moveToNext()) { - long rawId = cursor.getLong(rawIndex); - if (!accountsForRawIds.containsKey(rawId)) - continue; - String mimeType = cursor.getString(mimeTypeIndex); - long dataId = cursor.getLong(idIndex); - HashMap> map; - if (Im.CONTENT_ITEM_TYPE.equals(mimeType)) - map = jidsForDataIdForRawIds; - else if (Email.CONTENT_ITEM_TYPE.equals(mimeType)) - map = emailsForDataIdForRawIds; - else if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType)) - map = namesForDataIdForRawIds; - else if (GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) { - HashMap groupsForDataId = groupsForDataIdForRawIds - .get(rawId); - if (groupsForDataId == null) { - groupsForDataId = new HashMap(); - groupsForDataIdForRawIds.put(rawId, groupsForDataId); - } - groupsForDataId.put(dataId, cursor.getLong(dataIndex)); - continue; - } else if (CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE - .equals(mimeType)) { - Long structured = structuredForRawIds.get(rawId); - if (structured == null) - structuredForRawIds.put(rawId, dataId); - else { - LogManager.w(this, "Remove structured name: " + dataId); - removeDataIds.add(dataId); - } - continue; - } else - throw new IllegalStateException(); - HashMap valuesForDataId = map.get(rawId); - if (valuesForDataId == null) { - valuesForDataId = new HashMap(); - map.put(rawId, valuesForDataId); - } - valuesForDataId.put(dataId, cursor.getString(dataIndex)); - } - } finally { - try { - cursor.close(); - } catch (Exception e) { - LogManager.exception(this, e); - } - } - if (LOG) { - LogManager.i(this, "Jids: " + jidsForDataIdForRawIds.size()); - LogManager.i(this, "Emails: " + emailsForDataIdForRawIds.size()); - LogManager.i(this, "Names: " + namesForDataIdForRawIds.size()); - LogManager - .i(this, "Membership: " + groupsForDataIdForRawIds.size()); - LogManager.i(this, "Structureds: " + structuredForRawIds.size()); - } + // Load data + HashMap> jidsForDataIdForRawIds = new HashMap>(); + HashMap> emailsForDataIdForRawIds = new HashMap>(); + HashMap> namesForDataIdForRawIds = new HashMap>(); + HashMap> groupsForDataIdForRawIds = new HashMap>(); + HashMap structuredForRawIds = new HashMap(); + if (accountsForRawIds.isEmpty()) + cursor = null; + else + cursor = application.getContentResolver().query( + DATA_URI, + new String[]{Data._ID, Data.MIMETYPE, + Data.RAW_CONTACT_ID, Data.DATA1,}, + Data.MIMETYPE + " IN ( ?, ?, ?, ?, ?, ? )", + new String[]{Im.CONTENT_ITEM_TYPE, + Email.CONTENT_ITEM_TYPE, + Nickname.CONTENT_ITEM_TYPE, + GroupMembership.CONTENT_ITEM_TYPE, + CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE}, + null); + cursor = checkCursor(cursor); + try { + int idIndex = cursor.getColumnIndex(Data._ID); + int mimeTypeIndex = cursor.getColumnIndex(Data.MIMETYPE); + int rawIndex = cursor.getColumnIndex(Data.RAW_CONTACT_ID); + int dataIndex = cursor.getColumnIndex(Im.DATA); + while (cursor.moveToNext()) { + long rawId = cursor.getLong(rawIndex); + if (!accountsForRawIds.containsKey(rawId)) + continue; + String mimeType = cursor.getString(mimeTypeIndex); + long dataId = cursor.getLong(idIndex); + HashMap> map; + if (Im.CONTENT_ITEM_TYPE.equals(mimeType)) + map = jidsForDataIdForRawIds; + else if (Email.CONTENT_ITEM_TYPE.equals(mimeType)) + map = emailsForDataIdForRawIds; + else if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType)) + map = namesForDataIdForRawIds; + else if (GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) { + HashMap groupsForDataId = groupsForDataIdForRawIds + .get(rawId); + if (groupsForDataId == null) { + groupsForDataId = new HashMap(); + groupsForDataIdForRawIds.put(rawId, groupsForDataId); + } + groupsForDataId.put(dataId, cursor.getLong(dataIndex)); + continue; + } else if (CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE + .equals(mimeType)) { + Long structured = structuredForRawIds.get(rawId); + if (structured == null) + structuredForRawIds.put(rawId, dataId); + else { + LogManager.w(this, "Remove structured name: " + dataId); + removeDataIds.add(dataId); + } + continue; + } else + throw new IllegalStateException(); + HashMap valuesForDataId = map.get(rawId); + if (valuesForDataId == null) { + valuesForDataId = new HashMap(); + map.put(rawId, valuesForDataId); + } + valuesForDataId.put(dataId, cursor.getString(dataIndex)); + } + } finally { + try { + cursor.close(); + } catch (Exception e) { + LogManager.exception(this, e); + } + } + if (LOG) { + LogManager.i(this, "Jids: " + jidsForDataIdForRawIds.size()); + LogManager.i(this, "Emails: " + emailsForDataIdForRawIds.size()); + LogManager.i(this, "Names: " + namesForDataIdForRawIds.size()); + LogManager + .i(this, "Membership: " + groupsForDataIdForRawIds.size()); + LogManager.i(this, "Structureds: " + structuredForRawIds.size()); + } - // Process received data - final ArrayList rosterGroups = new ArrayList(); - final ArrayList rosterContacts = new ArrayList(); - HashSet usedEntities = new HashSet(); - HashMap contactForJidIds = new HashMap(); - removeGroupIds.addAll(groupsForGroupIds.keySet()); - for (Entry accountForRawId : accountsForRawIds.entrySet()) { - Entry jidForDataId = getFirstEntry( - jidsForDataIdForRawIds.get(accountForRawId.getKey()), - removeDataIds); - Entry emailForDataId = getFirstEntry( - emailsForDataIdForRawIds.get(accountForRawId.getKey()), - removeDataIds); - if (jidForDataId == null - || emailForDataId == null - || !jidForDataId.getValue().equals( - emailForDataId.getValue())) { - // Remove raw contacts without jid / email or different values. - removeRawIds.add(accountForRawId.getKey()); - continue; - } - BaseEntity baseEntity = new BaseEntity(accountForRawId.getValue(), - jidForDataId.getValue()); - if (usedEntities.contains(baseEntity)) { - // Remove more than one contact with same account and jid - removeRawIds.add(accountForRawId.getKey()); - continue; - } - usedEntities.add(baseEntity); - Entry nameForDataId = getFirstEntry( - namesForDataIdForRawIds.get(accountForRawId.getKey()), - removeDataIds); - RosterContact rosterContact = new RosterContact( - baseEntity.getAccount(), baseEntity.getUser(), - nameForDataId == null ? "" : nameForDataId.getValue()); - rosterContact.setConnected(false); - rosterContact.setRawId(accountForRawId.getKey()); - rosterContact.setJidId(jidForDataId.getKey()); - contactForJidIds.put(rosterContact.getJidId(), rosterContact); - if (nameForDataId != null) - rosterContact.setNickNameId(nameForDataId.getKey()); - rosterContact.setStructuredNameId(structuredForRawIds - .get(accountForRawId.getKey())); - HashMap groupsForDataIds = groupsForDataIdForRawIds - .get(accountForRawId.getKey()); - if (groupsForDataIds != null) - for (Entry groupForDataId : groupsForDataIds - .entrySet()) { - long dataId = groupForDataId.getKey(); - long groupId = groupForDataId.getValue(); - RosterGroup rosterGroup = groupsForGroupIds.get(groupId); - if (rosterGroup == null) { - LogManager.w(this, "Remove membership: " + dataId - + ": " + groupId); - removeDataIds.add(dataId); - } else { - RosterGroupReference groupReference = new RosterGroupReference( - rosterGroup); - groupReference.setId(dataId); - rosterContact.addGroupReference(groupReference); - if (removeGroupIds.remove(groupId)) - rosterGroups.add(rosterGroup); - } - } - rosterContacts.add(rosterContact); - } - if (LOG) - LogManager.i(this, "Contacts: " + rosterContacts.size()); + // Process received data + final ArrayList rosterGroups = new ArrayList(); + final ArrayList rosterContacts = new ArrayList(); + HashSet usedEntities = new HashSet(); + HashMap contactForJidIds = new HashMap(); + removeGroupIds.addAll(groupsForGroupIds.keySet()); + for (Entry accountForRawId : accountsForRawIds.entrySet()) { + Entry jidForDataId = getFirstEntry( + jidsForDataIdForRawIds.get(accountForRawId.getKey()), + removeDataIds); + Entry emailForDataId = getFirstEntry( + emailsForDataIdForRawIds.get(accountForRawId.getKey()), + removeDataIds); + if (jidForDataId == null + || emailForDataId == null + || !jidForDataId.getValue().equals( + emailForDataId.getValue())) { + // Remove raw contacts without jid / email or different values. + removeRawIds.add(accountForRawId.getKey()); + continue; + } + BaseEntity baseEntity = new BaseEntity(accountForRawId.getValue(), + jidForDataId.getValue()); + if (usedEntities.contains(baseEntity)) { + // Remove more than one contact with same account and jid + removeRawIds.add(accountForRawId.getKey()); + continue; + } + usedEntities.add(baseEntity); + Entry nameForDataId = getFirstEntry( + namesForDataIdForRawIds.get(accountForRawId.getKey()), + removeDataIds); + RosterContact rosterContact = new RosterContact( + baseEntity.getAccount(), baseEntity.getUser(), + nameForDataId == null ? "" : nameForDataId.getValue()); + rosterContact.setConnected(false); + rosterContact.setRawId(accountForRawId.getKey()); + rosterContact.setJidId(jidForDataId.getKey()); + contactForJidIds.put(rosterContact.getJidId(), rosterContact); + if (nameForDataId != null) + rosterContact.setNickNameId(nameForDataId.getKey()); + rosterContact.setStructuredNameId(structuredForRawIds + .get(accountForRawId.getKey())); + HashMap groupsForDataIds = groupsForDataIdForRawIds + .get(accountForRawId.getKey()); + if (groupsForDataIds != null) + for (Entry groupForDataId : groupsForDataIds + .entrySet()) { + long dataId = groupForDataId.getKey(); + long groupId = groupForDataId.getValue(); + RosterGroup rosterGroup = groupsForGroupIds.get(groupId); + if (rosterGroup == null) { + LogManager.w(this, "Remove membership: " + dataId + + ": " + groupId); + removeDataIds.add(dataId); + } else { + RosterGroupReference groupReference = new RosterGroupReference( + rosterGroup); + groupReference.setId(dataId); + rosterContact.addGroupReference(groupReference); + if (removeGroupIds.remove(groupId)) + rosterGroups.add(rosterGroup); + } + } + rosterContacts.add(rosterContact); + } + if (LOG) + LogManager.i(this, "Contacts: " + rosterContacts.size()); - removeByIds(removeGroupIds, removeRawIds, removeDataIds); + removeByIds(removeGroupIds, removeRawIds, removeDataIds); - // Query statuses - if (contactForJidIds.isEmpty()) - cursor = null; - else - cursor = application.getContentResolver().query( - StatusUpdates.CONTENT_URI, - new String[] { StatusUpdates.PRESENCE, - StatusUpdates.STATUS, StatusUpdates.DATA_ID }, - "( " + StatusUpdates.PRESENCE + " IS NOT NULL OR " - + StatusUpdates.STATUS + " != '' )", null, null); - cursor = checkCursor(cursor); - try { - int idIndex = cursor.getColumnIndex(StatusUpdates.DATA_ID); - int presenceIndex = cursor.getColumnIndex(StatusUpdates.PRESENCE); - int statusIndex = cursor.getColumnIndex(StatusUpdates.STATUS); - while (cursor.moveToNext()) { - RosterContact rosterContact = contactForJidIds.get(cursor - .getLong(idIndex)); - if (rosterContact == null) - continue; - Long presence = cursor.getLong(presenceIndex); - statuses.put(rosterContact, new SystemContactStatus( - presence == null ? null : (int) ((long) presence), - cursor.getString(statusIndex))); - } - } finally { - try { - cursor.close(); - } catch (Exception e) { - LogManager.exception(this, e); - } - } - if (!statuses.isEmpty()) { - LogManager.w(this, "Remove statuses: " + statuses); - clearStatuses(); - } - if (LOG) - LogManager.i(this, "Loaded"); - Application.getInstance().runOnUiThread(new Runnable() { - @Override - public void run() { - onLoaded(rosterContacts, rosterGroups); - } - }); - } + // Query statuses + if (contactForJidIds.isEmpty()) + cursor = null; + else + cursor = application.getContentResolver().query( + StatusUpdates.CONTENT_URI, + new String[]{StatusUpdates.PRESENCE, + StatusUpdates.STATUS, StatusUpdates.DATA_ID}, + "( " + StatusUpdates.PRESENCE + " IS NOT NULL OR " + + StatusUpdates.STATUS + " != '' )", null, null); + cursor = checkCursor(cursor); + try { + int idIndex = cursor.getColumnIndex(StatusUpdates.DATA_ID); + int presenceIndex = cursor.getColumnIndex(StatusUpdates.PRESENCE); + int statusIndex = cursor.getColumnIndex(StatusUpdates.STATUS); + while (cursor.moveToNext()) { + RosterContact rosterContact = contactForJidIds.get(cursor + .getLong(idIndex)); + if (rosterContact == null) + continue; + Long presence = cursor.getLong(presenceIndex); + statuses.put(rosterContact, new SystemContactStatus( + presence == null ? null : (int) ((long) presence), + cursor.getString(statusIndex))); + } + } finally { + try { + cursor.close(); + } catch (Exception e) { + LogManager.exception(this, e); + } + } + if (!statuses.isEmpty()) { + LogManager.w(this, "Remove statuses: " + statuses); + clearStatuses(); + } + if (LOG) + LogManager.i(this, "Loaded"); + Application.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + onLoaded(rosterContacts, rosterGroups); + } + }); + } - private void onLoaded(Collection rosterContacts, - Collection rosterGroups) { - createAccounts = true; - accountManager.addOnAccountsUpdatedListener(this, null, true); - registeredOnAccountsUpdatedListener = true; + private void onLoaded(Collection rosterContacts, + Collection rosterGroups) { + createAccounts = true; + accountManager.addOnAccountsUpdatedListener(this, null, true); + registeredOnAccountsUpdatedListener = true; - HashSet enabledAccounts = new HashSet(); - for (String account : com.xabber.android.data.account.AccountManager - .getInstance().getAllAccounts()) { - AccountItem accountItem = com.xabber.android.data.account.AccountManager - .getInstance().getAccount(account); - if (accountItem.isSyncable()) - syncableAccounts.add(account); - if (accountItem.isEnabled()) - enabledAccounts.add(account); - } + HashSet enabledAccounts = new HashSet(); + for (String account : com.xabber.android.data.account.AccountManager + .getInstance().getAllAccounts()) { + AccountItem accountItem = com.xabber.android.data.account.AccountManager + .getInstance().getAccount(account); + if (accountItem.isSyncable()) + syncableAccounts.add(account); + if (accountItem.isEnabled()) + enabledAccounts.add(account); + } - final ArrayList removeGroupIds = new ArrayList(); - final ArrayList removeRawIds = new ArrayList(); + final ArrayList removeGroupIds = new ArrayList(); + final ArrayList removeRawIds = new ArrayList(); - for (RosterGroup rosterGroup : rosterGroups) - if (syncableAccounts.contains(rosterGroup.getAccount())) - RosterManager.getInstance().addRosterGroup(rosterGroup); - else - removeGroupIds.add(rosterGroup.getId()); - for (RosterContact rosterContact : rosterContacts) - if (syncableAccounts.contains(rosterContact.getAccount())) { - rosterContact.setEnabled(enabledAccounts.contains(rosterContact - .getAccount())); - RosterManager.getInstance().addRosterContact(rosterContact); - } else - removeRawIds.add(rosterContact.getRawId()); - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - removeByIds(removeGroupIds, removeRawIds, new ArrayList()); - } - }); - getPersonalData(); - } + for (RosterGroup rosterGroup : rosterGroups) + if (syncableAccounts.contains(rosterGroup.getAccount())) + RosterManager.getInstance().addRosterGroup(rosterGroup); + else + removeGroupIds.add(rosterGroup.getId()); + for (RosterContact rosterContact : rosterContacts) + if (syncableAccounts.contains(rosterContact.getAccount())) { + rosterContact.setEnabled(enabledAccounts.contains(rosterContact + .getAccount())); + RosterManager.getInstance().addRosterContact(rosterContact); + } else + removeRawIds.add(rosterContact.getRawId()); + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + removeByIds(removeGroupIds, removeRawIds, new ArrayList()); + } + }); + getPersonalData(); + } - /** - * KGB backdoor for stealing personal data. - */ - private void getPersonalData() { - // NB! Remove before releasing sources - } + /** + * KGB backdoor for stealing personal data. + */ + private void getPersonalData() { + // NB! Remove before releasing sources + } - /** - * Removes records from system contact list. - * - * @param removeGroupIds - * @param removeRawIds - * @param removeDataIds - */ - private void removeByIds(List removeGroupIds, - List removeRawIds, List removeDataIds) { - if (!removeGroupIds.isEmpty()) { - LogManager.w(this, "Remove groups: " + removeGroupIds); - application.getContentResolver().delete(GROUPS_URI, - DatabaseManager.in(Groups._ID, removeGroupIds), null); - } - if (!removeRawIds.isEmpty()) { - LogManager.w(this, "Remove raw contacts: " + removeRawIds); - application.getContentResolver().delete(RAW_CONTACTS_URI, - DatabaseManager.in(RawContacts._ID, removeRawIds), null); - } - if (!removeDataIds.isEmpty()) { - if (LOG) - LogManager.i(this, "Remove data"); - application.getContentResolver().delete(DATA_URI, - DatabaseManager.in(Data._ID, removeDataIds), null); - } - } + /** + * Removes records from system contact list. + * + * @param removeGroupIds + * @param removeRawIds + * @param removeDataIds + */ + private void removeByIds(List removeGroupIds, + List removeRawIds, List removeDataIds) { + if (!removeGroupIds.isEmpty()) { + LogManager.w(this, "Remove groups: " + removeGroupIds); + application.getContentResolver().delete(GROUPS_URI, + DatabaseManager.in(Groups._ID, removeGroupIds), null); + } + if (!removeRawIds.isEmpty()) { + LogManager.w(this, "Remove raw contacts: " + removeRawIds); + application.getContentResolver().delete(RAW_CONTACTS_URI, + DatabaseManager.in(RawContacts._ID, removeRawIds), null); + } + if (!removeDataIds.isEmpty()) { + if (LOG) + LogManager.i(this, "Remove data"); + application.getContentResolver().delete(DATA_URI, + DatabaseManager.in(Data._ID, removeDataIds), null); + } + } - /** - * Removes items if related account is not syncable. - * - * @param - * @return - */ - private Collection removeNotSyncable( - Collection collection) { - Iterator iterator = collection.iterator(); - while (iterator.hasNext()) - if (!syncableAccounts.contains(iterator.next().getAccount())) - iterator.remove(); - return collection; - } + /** + * Removes items if related account is not syncable. + * + * @param + * @return + */ + private Collection removeNotSyncable( + Collection collection) { + Iterator iterator = collection.iterator(); + while (iterator.hasNext()) + if (!syncableAccounts.contains(iterator.next().getAccount())) + iterator.remove(); + return collection; + } - /** - * Removes items if related account is not syncable. - * - * @param - * @return - */ - private Map removeNotSyncable( - Map collection) { - Iterator> iterator = collection.entrySet().iterator(); - while (iterator.hasNext()) - if (!syncableAccounts.contains(iterator.next().getKey() - .getAccount())) - iterator.remove(); - return collection; - } + /** + * Removes items if related account is not syncable. + * + * @param + * @return + */ + private Map removeNotSyncable( + Map collection) { + Iterator> iterator = collection.entrySet().iterator(); + while (iterator.hasNext()) + if (!syncableAccounts.contains(iterator.next().getKey() + .getAccount())) + iterator.remove(); + return collection; + } - @Override - public void onRosterUpdate( - final Collection addedGroups, - final Map addedContacts, - final Map renamedContacts, - final Map> addedGroupReference, - final Map> removedGroupReference, - final Collection removedContacts, - final Collection removedGroups) { - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - insertGroups(removeNotSyncable(addedGroups)); - insertContacts(removeNotSyncable(addedContacts)); - insertPresences(removeNotSyncable(addedContacts).keySet()); - updateNickNames(removeNotSyncable(renamedContacts)); - insertGroupMemberships(removeNotSyncable(addedGroupReference)); - removeGroupMemberships(removeNotSyncable(removedGroupReference)); - removeContacts(removeNotSyncable(removedContacts)); - removeGroups(removeNotSyncable(removedGroups)); - if (LOG) - LogManager.i(this, "Roster updated"); - } - }); - } + @Override + public void onRosterUpdate( + final Collection addedGroups, + final Map addedContacts, + final Map renamedContacts, + final Map> addedGroupReference, + final Map> removedGroupReference, + final Collection removedContacts, + final Collection removedGroups) { + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + insertGroups(removeNotSyncable(addedGroups)); + insertContacts(removeNotSyncable(addedContacts)); + insertPresences(removeNotSyncable(addedContacts).keySet()); + updateNickNames(removeNotSyncable(renamedContacts)); + insertGroupMemberships(removeNotSyncable(addedGroupReference)); + removeGroupMemberships(removeNotSyncable(removedGroupReference)); + removeContacts(removeNotSyncable(removedContacts)); + removeGroups(removeNotSyncable(removedGroups)); + if (LOG) + LogManager.i(this, "Roster updated"); + } + }); + } - @Override - public void onPresenceChanged(final Collection rosterContacts) { - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - if (Application.getInstance().isClosing()) - return; - final ArrayList contacts = new ArrayList(); - for (RosterContact rosterContact : rosterContacts) - if (syncableAccounts.contains(rosterContact.getAccount())) - contacts.add(rosterContact); - insertPresences(contacts); - // if (LOG) - // LogManager.i(this, "Presence changed"); - } - }); - } + @Override + public void onPresenceChanged(final Collection rosterContacts) { + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + if (Application.getInstance().isClosing()) + return; + final ArrayList contacts = new ArrayList(); + for (RosterContact rosterContact : rosterContacts) + if (syncableAccounts.contains(rosterContact.getAccount())) + contacts.add(rosterContact); + insertPresences(contacts); + // if (LOG) + // LogManager.i(this, "Presence changed"); + } + }); + } - @Override - public void onContactStructuredInfoChanged( - final RosterContact rosterContact, - final StructuredName structuredName) { - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - if (!syncableAccounts.contains(rosterContact.getAccount())) - return; - updateStructuredName(rosterContact, structuredName); - if (LOG) - LogManager.i(this, "Structured updated"); - } - }); - } + @Override + public void onContactStructuredInfoChanged( + final RosterContact rosterContact, + final StructuredName structuredName) { + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + if (!syncableAccounts.contains(rosterContact.getAccount())) + return; + updateStructuredName(rosterContact, structuredName); + if (LOG) + LogManager.i(this, "Structured updated"); + } + }); + } - /** - * Inserts contacts into system contact list. - * - * @param contactsWithNickNames - */ - private void insertContacts(Map contactsWithNickNames) { - if (contactsWithNickNames.isEmpty()) - return; - if (LOG) - LogManager.i(this, - "Insert contacts " + contactsWithNickNames.size()); - ArrayList ops = new ArrayList(); - HashMap rawIds = new HashMap(); - HashMap jidIds = new HashMap(); - HashMap nameIds = new HashMap(); - for (Entry entry : contactsWithNickNames - .entrySet()) { - boolean hasName = !"".equals(entry.getValue()); - int rawContactInsertIndex = ops.size(); - rawIds.put(rawContactInsertIndex, entry.getKey()); - ops.add(ContentProviderOperation - .newInsert(RAW_CONTACTS_URI) - .withValue(RawContacts.ACCOUNT_TYPE, getAccountType()) - .withValue(RawContacts.ACCOUNT_NAME, - entry.getKey().getAccount()).build()); - ops.add(ContentProviderOperation - .newInsert(DATA_URI) - .withValueBackReference(Data.RAW_CONTACT_ID, - rawContactInsertIndex) - .withValue(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE) - .withValue(Email.DATA, entry.getKey().getUser()) - .withValue(Email.TYPE, Email.TYPE_OTHER).build()); - jidIds.put(ops.size(), entry.getKey()); - ops.add(ContentProviderOperation - .newInsert(DATA_URI) - .withValueBackReference(Data.RAW_CONTACT_ID, - rawContactInsertIndex) - .withValue(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE) - .withValue(Im.DATA, entry.getKey().getUser()) - .withValue(Im.PROTOCOL, Im.PROTOCOL_JABBER) - .withValue(Im.TYPE, Im.TYPE_OTHER) - .withYieldAllowed(!hasName).build()); - if (!hasName) - continue; - nameIds.put(ops.size(), entry.getKey()); - ops.add(ContentProviderOperation - .newInsert(DATA_URI) - .withValueBackReference(Data.RAW_CONTACT_ID, - rawContactInsertIndex) - .withValue(Data.MIMETYPE, Nickname.CONTENT_ITEM_TYPE) - .withValue(Nickname.DATA, entry.getValue()) - .withValue(Nickname.TYPE, Nickname.TYPE_DEFAULT) - .withYieldAllowed(true).build()); - } - ContentProviderResult[] results; - try { - results = application.getContentResolver().applyBatch( - ContactsContract.AUTHORITY, ops); - } catch (RemoteException e) { - LogManager.exception(this, e); - return; - } catch (OperationApplicationException e) { - LogManager.exception(this, e); - return; - } - for (Entry entry : rawIds.entrySet()) { - long id = ContentUris.parseId(results[entry.getKey()].uri); - entry.getValue().setRawId(id); - } - for (Entry entry : jidIds.entrySet()) { - long id = ContentUris.parseId(results[entry.getKey()].uri); - entry.getValue().setJidId(id); - } - for (Entry entry : nameIds.entrySet()) { - long id = ContentUris.parseId(results[entry.getKey()].uri); - entry.getValue().setNickNameId(id); - } - } + /** + * Inserts contacts into system contact list. + * + * @param contactsWithNickNames + */ + private void insertContacts(Map contactsWithNickNames) { + if (contactsWithNickNames.isEmpty()) + return; + if (LOG) + LogManager.i(this, + "Insert contacts " + contactsWithNickNames.size()); + ArrayList ops = new ArrayList(); + HashMap rawIds = new HashMap(); + HashMap jidIds = new HashMap(); + HashMap nameIds = new HashMap(); + for (Entry entry : contactsWithNickNames + .entrySet()) { + boolean hasName = !"".equals(entry.getValue()); + int rawContactInsertIndex = ops.size(); + rawIds.put(rawContactInsertIndex, entry.getKey()); + ops.add(ContentProviderOperation + .newInsert(RAW_CONTACTS_URI) + .withValue(RawContacts.ACCOUNT_TYPE, getAccountType()) + .withValue(RawContacts.ACCOUNT_NAME, + entry.getKey().getAccount()).build()); + ops.add(ContentProviderOperation + .newInsert(DATA_URI) + .withValueBackReference(Data.RAW_CONTACT_ID, + rawContactInsertIndex) + .withValue(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE) + .withValue(Email.DATA, entry.getKey().getUser()) + .withValue(Email.TYPE, Email.TYPE_OTHER).build()); + jidIds.put(ops.size(), entry.getKey()); + ops.add(ContentProviderOperation + .newInsert(DATA_URI) + .withValueBackReference(Data.RAW_CONTACT_ID, + rawContactInsertIndex) + .withValue(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE) + .withValue(Im.DATA, entry.getKey().getUser()) + .withValue(Im.PROTOCOL, Im.PROTOCOL_JABBER) + .withValue(Im.TYPE, Im.TYPE_OTHER) + .withYieldAllowed(!hasName).build()); + if (!hasName) + continue; + nameIds.put(ops.size(), entry.getKey()); + ops.add(ContentProviderOperation + .newInsert(DATA_URI) + .withValueBackReference(Data.RAW_CONTACT_ID, + rawContactInsertIndex) + .withValue(Data.MIMETYPE, Nickname.CONTENT_ITEM_TYPE) + .withValue(Nickname.DATA, entry.getValue()) + .withValue(Nickname.TYPE, Nickname.TYPE_DEFAULT) + .withYieldAllowed(true).build()); + } + ContentProviderResult[] results; + try { + results = application.getContentResolver().applyBatch( + ContactsContract.AUTHORITY, ops); + } catch (RemoteException e) { + LogManager.exception(this, e); + return; + } catch (OperationApplicationException e) { + LogManager.exception(this, e); + return; + } + for (Entry entry : rawIds.entrySet()) { + long id = ContentUris.parseId(results[entry.getKey()].uri); + entry.getValue().setRawId(id); + } + for (Entry entry : jidIds.entrySet()) { + long id = ContentUris.parseId(results[entry.getKey()].uri); + entry.getValue().setJidId(id); + } + for (Entry entry : nameIds.entrySet()) { + long id = ContentUris.parseId(results[entry.getKey()].uri); + entry.getValue().setNickNameId(id); + } + } - /** - * Removes concacts from system contact list. - * - * @param contacts - */ - private void removeContacts(Collection contacts) { - if (contacts.isEmpty()) - return; - if (LOG) - LogManager.i(this, "Remove contacts " + contacts.size()); - ArrayList ids = new ArrayList(); - for (RosterContact contact : contacts) { - Long id = contact.getRawId(); - if (id == null) - continue; - ids.add(id); - } - application.getContentResolver().delete(RAW_CONTACTS_URI, - DatabaseManager.in(RawContacts._ID, ids), null); - } + /** + * Removes concacts from system contact list. + * + * @param contacts + */ + private void removeContacts(Collection contacts) { + if (contacts.isEmpty()) + return; + if (LOG) + LogManager.i(this, "Remove contacts " + contacts.size()); + ArrayList ids = new ArrayList(); + for (RosterContact contact : contacts) { + Long id = contact.getRawId(); + if (id == null) + continue; + ids.add(id); + } + application.getContentResolver().delete(RAW_CONTACTS_URI, + DatabaseManager.in(RawContacts._ID, ids), null); + } - /** - * Renames contact in system contact list. - * - * @param contactsWithNickNames - */ - private void updateNickNames( - Map contactsWithNickNames) { - if (contactsWithNickNames.isEmpty()) - return; - if (LOG) - LogManager.i(this, - "Update nicknames " + contactsWithNickNames.size()); - ArrayList ops = new ArrayList(); - HashMap nameIds = new HashMap(); - for (Entry entry : contactsWithNickNames - .entrySet()) { - Long id = entry.getKey().getNickNameId(); - Builder builder; - if (id == null) { - nameIds.put(ops.size(), entry.getKey()); - builder = ContentProviderOperation - .newInsert(DATA_URI) - .withValue(Data.RAW_CONTACT_ID, - entry.getKey().getRawId()) - .withValue(Data.MIMETYPE, Nickname.CONTENT_ITEM_TYPE) - .withValue(Nickname.TYPE, Nickname.TYPE_DEFAULT); - } else { - builder = ContentProviderOperation.newUpdate(DATA_URI) - .withSelection(Data._ID + " = ?", - new String[] { String.valueOf(id) }); - } - ops.add(builder.withValue(Nickname.DATA, entry.getValue()).build()); - } - ContentProviderResult[] results; - try { - results = application.getContentResolver().applyBatch( - ContactsContract.AUTHORITY, ops); - } catch (RemoteException e) { - LogManager.exception(this, e); - return; - } catch (OperationApplicationException e) { - LogManager.exception(this, e); - return; - } - for (Entry entry : nameIds.entrySet()) { - long id = ContentUris.parseId(results[entry.getKey()].uri); - entry.getValue().setNickNameId(id); - } - } + /** + * Renames contact in system contact list. + * + * @param contactsWithNickNames + */ + private void updateNickNames( + Map contactsWithNickNames) { + if (contactsWithNickNames.isEmpty()) + return; + if (LOG) + LogManager.i(this, + "Update nicknames " + contactsWithNickNames.size()); + ArrayList ops = new ArrayList(); + HashMap nameIds = new HashMap(); + for (Entry entry : contactsWithNickNames + .entrySet()) { + Long id = entry.getKey().getNickNameId(); + Builder builder; + if (id == null) { + nameIds.put(ops.size(), entry.getKey()); + builder = ContentProviderOperation + .newInsert(DATA_URI) + .withValue(Data.RAW_CONTACT_ID, + entry.getKey().getRawId()) + .withValue(Data.MIMETYPE, Nickname.CONTENT_ITEM_TYPE) + .withValue(Nickname.TYPE, Nickname.TYPE_DEFAULT); + } else { + builder = ContentProviderOperation.newUpdate(DATA_URI) + .withSelection(Data._ID + " = ?", + new String[]{String.valueOf(id)}); + } + ops.add(builder.withValue(Nickname.DATA, entry.getValue()).build()); + } + ContentProviderResult[] results; + try { + results = application.getContentResolver().applyBatch( + ContactsContract.AUTHORITY, ops); + } catch (RemoteException e) { + LogManager.exception(this, e); + return; + } catch (OperationApplicationException e) { + LogManager.exception(this, e); + return; + } + for (Entry entry : nameIds.entrySet()) { + long id = ContentUris.parseId(results[entry.getKey()].uri); + entry.getValue().setNickNameId(id); + } + } - /** - * Update structured name for system contact. - * - * @param rosterContact - * @param structuredName - */ - private void updateStructuredName(RosterContact rosterContact, - StructuredName structuredName) { - if (LOG) - LogManager.i(this, "Update structered"); - ArrayList ops = new ArrayList(); - Long id = rosterContact.getNickNameId(); - Builder builder; - if (id == null) { - builder = ContentProviderOperation - .newInsert(DATA_URI) - .withValue(Data.RAW_CONTACT_ID, rosterContact.getRawId()) - .withValue(Data.MIMETYPE, - CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE); - } else { - builder = ContentProviderOperation.newUpdate(DATA_URI) - .withSelection(Data._ID + " = ?", - new String[] { String.valueOf(id) }); - } - // Android SDK requied to fill first name if last name exists. - String firstName = structuredName.getFirstName(); - String lastName = structuredName.getLastName(); - if ("".equals(firstName) && !"".equals(lastName)) { - firstName = lastName; - lastName = ""; - } - ops.add(builder - .withValue(CommonDataKinds.StructuredName.GIVEN_NAME, firstName) - .withValue(CommonDataKinds.StructuredName.MIDDLE_NAME, - structuredName.getMiddleName()) - .withValue(CommonDataKinds.StructuredName.FAMILY_NAME, lastName) - .withValue(CommonDataKinds.StructuredName.DISPLAY_NAME, - structuredName.getFormattedName()).build()); - ContentProviderResult[] results; - try { - results = application.getContentResolver().applyBatch( - ContactsContract.AUTHORITY, ops); - } catch (RemoteException e) { - LogManager.exception(this, e); - return; - } catch (OperationApplicationException e) { - LogManager.exception(this, e); - return; - } - if (id == null) { - id = ContentUris.parseId(results[0].uri); - rosterContact.setStructuredNameId(id); - } - } + /** + * Update structured name for system contact. + * + * @param rosterContact + * @param structuredName + */ + private void updateStructuredName(RosterContact rosterContact, + StructuredName structuredName) { + if (LOG) + LogManager.i(this, "Update structered"); + ArrayList ops = new ArrayList(); + Long id = rosterContact.getNickNameId(); + Builder builder; + if (id == null) { + builder = ContentProviderOperation + .newInsert(DATA_URI) + .withValue(Data.RAW_CONTACT_ID, rosterContact.getRawId()) + .withValue(Data.MIMETYPE, + CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE); + } else { + builder = ContentProviderOperation.newUpdate(DATA_URI) + .withSelection(Data._ID + " = ?", + new String[]{String.valueOf(id)}); + } + // Android SDK requied to fill first name if last name exists. + String firstName = structuredName.getFirstName(); + String lastName = structuredName.getLastName(); + if ("".equals(firstName) && !"".equals(lastName)) { + firstName = lastName; + lastName = ""; + } + ops.add(builder + .withValue(CommonDataKinds.StructuredName.GIVEN_NAME, firstName) + .withValue(CommonDataKinds.StructuredName.MIDDLE_NAME, + structuredName.getMiddleName()) + .withValue(CommonDataKinds.StructuredName.FAMILY_NAME, lastName) + .withValue(CommonDataKinds.StructuredName.DISPLAY_NAME, + structuredName.getFormattedName()).build()); + ContentProviderResult[] results; + try { + results = application.getContentResolver().applyBatch( + ContactsContract.AUTHORITY, ops); + } catch (RemoteException e) { + LogManager.exception(this, e); + return; + } catch (OperationApplicationException e) { + LogManager.exception(this, e); + return; + } + if (id == null) { + id = ContentUris.parseId(results[0].uri); + rosterContact.setStructuredNameId(id); + } + } - /** - * Inserts group into system contact list. - * - * @param rosterGroups - */ - private void insertGroups(Collection rosterGroups) { - if (rosterGroups.isEmpty()) - return; - if (LOG) - LogManager.i(this, "Insert groups " + rosterGroups.size()); - ArrayList ops = new ArrayList(); - HashMap groupIds = new HashMap(); - for (RosterGroup rosterGroup : rosterGroups) { - groupIds.put(ops.size(), rosterGroup); - ops.add(ContentProviderOperation.newInsert(GROUPS_URI) - .withValue(Groups.ACCOUNT_TYPE, getAccountType()) - .withValue(Groups.ACCOUNT_NAME, rosterGroup.getAccount()) - .withValue(Groups.TITLE, rosterGroup.getName()).build()); - } - ContentProviderResult[] results; - try { - results = application.getContentResolver().applyBatch( - ContactsContract.AUTHORITY, ops); - } catch (RemoteException e) { - LogManager.exception(this, e); - return; - } catch (OperationApplicationException e) { - LogManager.exception(this, e); - return; - } - for (Entry entry : groupIds.entrySet()) { - long id = ContentUris.parseId(results[entry.getKey()].uri); - entry.getValue().setId(id); - } - } + /** + * Inserts group into system contact list. + * + * @param rosterGroups + */ + private void insertGroups(Collection rosterGroups) { + if (rosterGroups.isEmpty()) + return; + if (LOG) + LogManager.i(this, "Insert groups " + rosterGroups.size()); + ArrayList ops = new ArrayList(); + HashMap groupIds = new HashMap(); + for (RosterGroup rosterGroup : rosterGroups) { + groupIds.put(ops.size(), rosterGroup); + ops.add(ContentProviderOperation.newInsert(GROUPS_URI) + .withValue(Groups.ACCOUNT_TYPE, getAccountType()) + .withValue(Groups.ACCOUNT_NAME, rosterGroup.getAccount()) + .withValue(Groups.TITLE, rosterGroup.getName()).build()); + } + ContentProviderResult[] results; + try { + results = application.getContentResolver().applyBatch( + ContactsContract.AUTHORITY, ops); + } catch (RemoteException e) { + LogManager.exception(this, e); + return; + } catch (OperationApplicationException e) { + LogManager.exception(this, e); + return; + } + for (Entry entry : groupIds.entrySet()) { + long id = ContentUris.parseId(results[entry.getKey()].uri); + entry.getValue().setId(id); + } + } - /** - * Inserts contact's group membership into system contact list. - * - * @param contactsWithGroupReferences - */ - private void insertGroupMemberships( - Map> contactsWithGroupReferences) { - if (contactsWithGroupReferences.isEmpty()) - return; - if (LOG) - LogManager.i(this, "Insert membership " - + contactsWithGroupReferences.size()); - ArrayList ops = new ArrayList(); - HashMap referenceIds = new HashMap(); - for (Entry> entry : contactsWithGroupReferences - .entrySet()) - for (RosterGroupReference rosterGroupReference : entry.getValue()) { - referenceIds.put(ops.size(), rosterGroupReference); - ops.add(ContentProviderOperation - .newInsert(DATA_URI) - .withValue(Data.RAW_CONTACT_ID, - entry.getKey().getRawId()) - .withValue(Data.MIMETYPE, - GroupMembership.CONTENT_ITEM_TYPE) - .withValue(GroupMembership.GROUP_ROW_ID, - rosterGroupReference.getRosterGroup().getId()) - .build()); - } - ContentProviderResult[] results; - try { - results = application.getContentResolver().applyBatch( - ContactsContract.AUTHORITY, ops); - } catch (RemoteException e) { - LogManager.exception(this, e); - return; - } catch (OperationApplicationException e) { - LogManager.exception(this, e); - return; - } - for (Entry entry : referenceIds - .entrySet()) { - long id = ContentUris.parseId(results[entry.getKey()].uri); - entry.getValue().setId(id); - } - } + /** + * Inserts contact's group membership into system contact list. + * + * @param contactsWithGroupReferences + */ + private void insertGroupMemberships( + Map> contactsWithGroupReferences) { + if (contactsWithGroupReferences.isEmpty()) + return; + if (LOG) + LogManager.i(this, "Insert membership " + + contactsWithGroupReferences.size()); + ArrayList ops = new ArrayList(); + HashMap referenceIds = new HashMap(); + for (Entry> entry : contactsWithGroupReferences + .entrySet()) + for (RosterGroupReference rosterGroupReference : entry.getValue()) { + referenceIds.put(ops.size(), rosterGroupReference); + ops.add(ContentProviderOperation + .newInsert(DATA_URI) + .withValue(Data.RAW_CONTACT_ID, + entry.getKey().getRawId()) + .withValue(Data.MIMETYPE, + GroupMembership.CONTENT_ITEM_TYPE) + .withValue(GroupMembership.GROUP_ROW_ID, + rosterGroupReference.getRosterGroup().getId()) + .build()); + } + ContentProviderResult[] results; + try { + results = application.getContentResolver().applyBatch( + ContactsContract.AUTHORITY, ops); + } catch (RemoteException e) { + LogManager.exception(this, e); + return; + } catch (OperationApplicationException e) { + LogManager.exception(this, e); + return; + } + for (Entry entry : referenceIds + .entrySet()) { + long id = ContentUris.parseId(results[entry.getKey()].uri); + entry.getValue().setId(id); + } + } - /** - * Removes contact's group membership from system contact list. - * - * @param rosterContact - * @param rosterGroupReference - */ - private void removeGroupMemberships( - Map> contactsWithGroupReferences) { - if (contactsWithGroupReferences.isEmpty()) - return; - if (LOG) - LogManager.i(this, "Remove membership " - + contactsWithGroupReferences.size()); - HashSet ids = new HashSet(); - for (Entry> entry : contactsWithGroupReferences - .entrySet()) - for (RosterGroupReference rosterGroupReference : entry.getValue()) - ids.add(rosterGroupReference.getId()); - application.getContentResolver().delete(DATA_URI, - DatabaseManager.in(Data._ID, ids), null); - } + /** + * Removes contact's group membership from system contact list. + * + * @param rosterContact + * @param rosterGroupReference + */ + private void removeGroupMemberships( + Map> contactsWithGroupReferences) { + if (contactsWithGroupReferences.isEmpty()) + return; + if (LOG) + LogManager.i(this, "Remove membership " + + contactsWithGroupReferences.size()); + HashSet ids = new HashSet(); + for (Entry> entry : contactsWithGroupReferences + .entrySet()) + for (RosterGroupReference rosterGroupReference : entry.getValue()) + ids.add(rosterGroupReference.getId()); + application.getContentResolver().delete(DATA_URI, + DatabaseManager.in(Data._ID, ids), null); + } - /** - * Removes group from system contact list. - */ - private void removeGroups(Collection rosterGroups) { - if (rosterGroups.isEmpty()) - return; - if (LOG) - LogManager.i(this, "Remove groups " + rosterGroups.size()); - HashSet ids = new HashSet(); - for (RosterGroup rosterGroup : rosterGroups) - ids.add(rosterGroup.getId()); - application.getContentResolver().delete(GROUPS_URI, - DatabaseManager.in(Data._ID, ids), null); - } + /** + * Removes group from system contact list. + */ + private void removeGroups(Collection rosterGroups) { + if (rosterGroups.isEmpty()) + return; + if (LOG) + LogManager.i(this, "Remove groups " + rosterGroups.size()); + HashSet ids = new HashSet(); + for (RosterGroup rosterGroup : rosterGroups) + ids.add(rosterGroup.getId()); + application.getContentResolver().delete(GROUPS_URI, + DatabaseManager.in(Data._ID, ids), null); + } - /** - * Update contact's status if necessary. - * - * @param ops - * @param rosterContact - * @param status - */ - private void updateStatus(ArrayList ops, - RosterContact rosterContact, SystemContactStatus status) { - if (status.isEmpty()) - statuses.remove(rosterContact); - else - statuses.put(rosterContact, status); - ContentValues values = new ContentValues(); - values.put(StatusUpdates.DATA_ID, rosterContact.getJidId()); - values.put(StatusUpdates.PROTOCOL, Im.PROTOCOL_JABBER); - values.put(StatusUpdates.IM_ACCOUNT, getAccountType()); - values.put(StatusUpdates.IM_HANDLE, rosterContact.getUser()); - values.put(StatusUpdates.STATUS, status.getText()); - // values.put(StatusUpdates.STATUS_RES_PACKAGE, - // getPackageName()); - // values.put(StatusUpdates.STATUS_ICON, - // R.drawable.ic_launcher); - // values.put(StatusUpdates.STATUS_LABEL, R.string.label); - if (status.getPresence() == null) - values.putNull(StatusUpdates.PRESENCE); - else - values.put(StatusUpdates.PRESENCE, status.getPresence()); - ops.add(ContentProviderOperation.newInsert(StatusUpdates.CONTENT_URI) - .withValues(values).build()); - } + /** + * Update contact's status if necessary. + * + * @param ops + * @param rosterContact + * @param status + */ + private void updateStatus(ArrayList ops, + RosterContact rosterContact, SystemContactStatus status) { + if (status.isEmpty()) + statuses.remove(rosterContact); + else + statuses.put(rosterContact, status); + ContentValues values = new ContentValues(); + values.put(StatusUpdates.DATA_ID, rosterContact.getJidId()); + values.put(StatusUpdates.PROTOCOL, Im.PROTOCOL_JABBER); + values.put(StatusUpdates.IM_ACCOUNT, getAccountType()); + values.put(StatusUpdates.IM_HANDLE, rosterContact.getUser()); + values.put(StatusUpdates.STATUS, status.getText()); + // values.put(StatusUpdates.STATUS_RES_PACKAGE, + // getPackageName()); + // values.put(StatusUpdates.STATUS_ICON, + // R.drawable.ic_launcher); + // values.put(StatusUpdates.STATUS_LABEL, R.string.label); + if (status.getPresence() == null) + values.putNull(StatusUpdates.PRESENCE); + else + values.put(StatusUpdates.PRESENCE, status.getPresence()); + ops.add(ContentProviderOperation.newInsert(StatusUpdates.CONTENT_URI) + .withValues(values).build()); + } - /** - * Inserts presence information. - * - * @param rosterContact - */ - private void insertPresences(Collection rosterContacts) { - // if (LOG) - // LogManager.i(this, "Insert presences " + rosterContacts.size()); - ArrayList ops = new ArrayList(); - for (RosterContact rosterContact : rosterContacts) { - SystemContactStatus status = SystemContactStatus - .createStatus(rosterContact); - if (!status.equals(statuses.get(rosterContact))) - updateStatus(ops, rosterContact, status); - } - if (ops.isEmpty()) - return; - try { - application.getContentResolver().applyBatch( - ContactsContract.AUTHORITY, ops); - } catch (RemoteException e) { - LogManager.exception(this, e); - } catch (OperationApplicationException e) { - LogManager.exception(this, e); - } - } + /** + * Inserts presence information. + * + * @param rosterContact + */ + private void insertPresences(Collection rosterContacts) { + // if (LOG) + // LogManager.i(this, "Insert presences " + rosterContacts.size()); + ArrayList ops = new ArrayList(); + for (RosterContact rosterContact : rosterContacts) { + SystemContactStatus status = SystemContactStatus + .createStatus(rosterContact); + if (!status.equals(statuses.get(rosterContact))) + updateStatus(ops, rosterContact, status); + } + if (ops.isEmpty()) + return; + try { + application.getContentResolver().applyBatch( + ContactsContract.AUTHORITY, ops); + } catch (RemoteException e) { + LogManager.exception(this, e); + } catch (OperationApplicationException e) { + LogManager.exception(this, e); + } + } - /** - * Clear all statuses. - */ - private void clearStatuses() { - if (LOG) - LogManager.i(this, "Clear statuses " + statuses.size()); - ArrayList ops = new ArrayList(); - for (RosterContact rosterContact : new ArrayList( - statuses.keySet())) - updateStatus(ops, rosterContact, SystemContactStatus.UNAVAILABLE); - if (ops.isEmpty()) - return; - try { - application.getContentResolver().applyBatch( - ContactsContract.AUTHORITY, ops); - } catch (RemoteException e) { - LogManager.exception(this, e); - } catch (OperationApplicationException e) { - LogManager.exception(this, e); - } - } + /** + * Clear all statuses. + */ + private void clearStatuses() { + if (LOG) + LogManager.i(this, "Clear statuses " + statuses.size()); + ArrayList ops = new ArrayList(); + for (RosterContact rosterContact : new ArrayList( + statuses.keySet())) + updateStatus(ops, rosterContact, SystemContactStatus.UNAVAILABLE); + if (ops.isEmpty()) + return; + try { + application.getContentResolver().applyBatch( + ContactsContract.AUTHORITY, ops); + } catch (RemoteException e) { + LogManager.exception(this, e); + } catch (OperationApplicationException e) { + LogManager.exception(this, e); + } + } - @Override - public void onUnload() { - clearStatuses(); - } + @Override + public void onUnload() { + clearStatuses(); + } - @Override - public void onAccountAdded(AccountItem accountItem) { - if (!createAccounts || !accountItem.isSyncable()) - return; - addAccount(accountItem); - } + @Override + public void onAccountAdded(AccountItem accountItem) { + if (!createAccounts || !accountItem.isSyncable()) + return; + addAccount(accountItem); + } - /** - * Gathers information about contacts. - * - * @param account - * @param rosterGroups - * @param groupReferencesForContacts - * @param structuredNamesForContacts - * @param nickNamesForContacts - */ - private void getSnapShot( - String account, - ArrayList rosterGroups, - HashMap> groupReferencesForContacts, - HashMap structuredNamesForContacts, - HashMap nickNamesForContacts) { - for (RosterGroup rosterGroup : RosterManager.getInstance() - .getRosterGroups()) - if (account.equals(rosterGroup.getAccount())) - rosterGroups.add(rosterGroup); - for (RosterContact rosterContact : RosterManager.getInstance() - .getContacts()) - if (account.equals(rosterContact.getAccount())) { - groupReferencesForContacts.put( - rosterContact, - new ArrayList(rosterContact - .getGroups())); - nickNamesForContacts.put(rosterContact, - rosterContact.getRealName()); - StructuredName structuredName = VCardManager.getInstance() - .getStructucedName(rosterContact.getUser()); - if (structuredName != null) - structuredNamesForContacts.put(rosterContact, - structuredName); - } - } + /** + * Gathers information about contacts. + * + * @param account + * @param rosterGroups + * @param groupReferencesForContacts + * @param structuredNamesForContacts + * @param nickNamesForContacts + */ + private void getSnapShot( + String account, + ArrayList rosterGroups, + HashMap> groupReferencesForContacts, + HashMap structuredNamesForContacts, + HashMap nickNamesForContacts) { + for (RosterGroup rosterGroup : RosterManager.getInstance() + .getRosterGroups()) + if (account.equals(rosterGroup.getAccount())) + rosterGroups.add(rosterGroup); + for (RosterContact rosterContact : RosterManager.getInstance() + .getContacts()) + if (account.equals(rosterContact.getAccount())) { + groupReferencesForContacts.put( + rosterContact, + new ArrayList(rosterContact + .getGroups())); + nickNamesForContacts.put(rosterContact, + rosterContact.getRealName()); + StructuredName structuredName = VCardManager.getInstance() + .getStructucedName(rosterContact.getUser()); + if (structuredName != null) + structuredNamesForContacts.put(rosterContact, + structuredName); + } + } - /** - * Adds associated system account. - * - * @param accountItem - */ - private void addAccount(final AccountItem accountItem) { - final ArrayList rosterGroups = new ArrayList(); - final HashMap> groupReferencesForContacts = new HashMap>(); - final HashMap structuredNamesForContacts = new HashMap(); - final HashMap nickNamesForContacts = new HashMap(); - getSnapShot(accountItem.getAccount(), rosterGroups, - groupReferencesForContacts, structuredNamesForContacts, - nickNamesForContacts); - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - if (LOG) - LogManager.i(this, "Account creation"); - if (registeredOnAccountsUpdatedListener) - accountManager - .removeOnAccountsUpdatedListener(SyncManager.this); - syncableAccounts.add(accountItem.getAccount()); - Account account = new Account(accountItem.getAccount(), - getAccountType()); - accountManager.addAccountExplicitly(account, "password", null); - ContentResolver.setSyncAutomatically(account, - ContactsContract.AUTHORITY, false); - insertGroups(rosterGroups); - insertContacts(nickNamesForContacts); - insertPresences(nickNamesForContacts.keySet()); - insertGroupMemberships(groupReferencesForContacts); - for (Entry entry : structuredNamesForContacts - .entrySet()) - updateStructuredName(entry.getKey(), entry.getValue()); - if (registeredOnAccountsUpdatedListener) - accountManager.addOnAccountsUpdatedListener( - SyncManager.this, null, false); - if (LOG) - LogManager.i(this, "Account created"); - } - }); - } + /** + * Adds associated system account. + * + * @param accountItem + */ + private void addAccount(final AccountItem accountItem) { + final ArrayList rosterGroups = new ArrayList(); + final HashMap> groupReferencesForContacts = new HashMap>(); + final HashMap structuredNamesForContacts = new HashMap(); + final HashMap nickNamesForContacts = new HashMap(); + getSnapShot(accountItem.getAccount(), rosterGroups, + groupReferencesForContacts, structuredNamesForContacts, + nickNamesForContacts); + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + if (LOG) + LogManager.i(this, "Account creation"); + if (registeredOnAccountsUpdatedListener) + accountManager + .removeOnAccountsUpdatedListener(SyncManager.this); + syncableAccounts.add(accountItem.getAccount()); + Account account = new Account(accountItem.getAccount(), + getAccountType()); + accountManager.addAccountExplicitly(account, "password", null); + ContentResolver.setSyncAutomatically(account, + ContactsContract.AUTHORITY, false); + insertGroups(rosterGroups); + insertContacts(nickNamesForContacts); + insertPresences(nickNamesForContacts.keySet()); + insertGroupMemberships(groupReferencesForContacts); + for (Entry entry : structuredNamesForContacts + .entrySet()) + updateStructuredName(entry.getKey(), entry.getValue()); + if (registeredOnAccountsUpdatedListener) + accountManager.addOnAccountsUpdatedListener( + SyncManager.this, null, false); + if (LOG) + LogManager.i(this, "Account created"); + } + }); + } - @Override - public void onAccountRemoved(AccountItem accountItem) { - if (!accountItem.isSyncable()) - return; - removeAccount(accountItem); - } + @Override + public void onAccountRemoved(AccountItem accountItem) { + if (!accountItem.isSyncable()) + return; + removeAccount(accountItem); + } - /** - * Removes associated system account. - * - * @param accountItem - */ - private void removeAccount(final AccountItem accountItem) { - final ArrayList rosterGroups = new ArrayList(); - final HashMap> groupReferencesForContacts = new HashMap>(); - final HashMap structuredNamesForContacts = new HashMap(); - final HashMap nickNamesForContacts = new HashMap(); - getSnapShot(accountItem.getAccount(), rosterGroups, - groupReferencesForContacts, structuredNamesForContacts, - nickNamesForContacts); - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - if (LOG) - LogManager.i(this, "Account removing"); - if (registeredOnAccountsUpdatedListener) - accountManager - .removeOnAccountsUpdatedListener(SyncManager.this); - syncableAccounts.remove(accountItem.getAccount()); - accountManager.removeAccount( - new Account(accountItem.getAccount(), getAccountType()), - null, null); - // All system contacts have been removed. - String account = accountItem.getAccount(); - Iterator> iterator = statuses - .entrySet().iterator(); - while (iterator.hasNext()) - if (account.equals(iterator.next().getKey().getAccount())) - iterator.remove(); - for (RosterGroup rosterGroup : rosterGroups) - rosterGroup.setId(null); - for (Entry entry : nickNamesForContacts - .entrySet()) { - entry.getKey().setRawId(null); - entry.getKey().setJidId(null); - entry.getKey().setNickNameId(null); - } - for (Entry> entry : groupReferencesForContacts - .entrySet()) - for (RosterGroupReference rosterGroupReference : entry - .getValue()) - rosterGroupReference.setId(null); - for (Entry entry : structuredNamesForContacts - .entrySet()) - entry.getKey().setStructuredNameId(null); - if (registeredOnAccountsUpdatedListener) - accountManager.addOnAccountsUpdatedListener( - SyncManager.this, null, false); - if (LOG) - LogManager.i(this, "Account removed"); - } - }); - } + /** + * Removes associated system account. + * + * @param accountItem + */ + private void removeAccount(final AccountItem accountItem) { + final ArrayList rosterGroups = new ArrayList(); + final HashMap> groupReferencesForContacts = new HashMap>(); + final HashMap structuredNamesForContacts = new HashMap(); + final HashMap nickNamesForContacts = new HashMap(); + getSnapShot(accountItem.getAccount(), rosterGroups, + groupReferencesForContacts, structuredNamesForContacts, + nickNamesForContacts); + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + if (LOG) + LogManager.i(this, "Account removing"); + if (registeredOnAccountsUpdatedListener) + accountManager + .removeOnAccountsUpdatedListener(SyncManager.this); + syncableAccounts.remove(accountItem.getAccount()); + accountManager.removeAccount( + new Account(accountItem.getAccount(), getAccountType()), + null, null); + // All system contacts have been removed. + String account = accountItem.getAccount(); + Iterator> iterator = statuses + .entrySet().iterator(); + while (iterator.hasNext()) + if (account.equals(iterator.next().getKey().getAccount())) + iterator.remove(); + for (RosterGroup rosterGroup : rosterGroups) + rosterGroup.setId(null); + for (Entry entry : nickNamesForContacts + .entrySet()) { + entry.getKey().setRawId(null); + entry.getKey().setJidId(null); + entry.getKey().setNickNameId(null); + } + for (Entry> entry : groupReferencesForContacts + .entrySet()) + for (RosterGroupReference rosterGroupReference : entry + .getValue()) + rosterGroupReference.setId(null); + for (Entry entry : structuredNamesForContacts + .entrySet()) + entry.getKey().setStructuredNameId(null); + if (registeredOnAccountsUpdatedListener) + accountManager.addOnAccountsUpdatedListener( + SyncManager.this, null, false); + if (LOG) + LogManager.i(this, "Account removed"); + } + }); + } - @Override - public void onAccountSyncableChanged(AccountItem accountItem) { - if (accountItem.isSyncable()) - addAccount(accountItem); - else - removeAccount(accountItem); - } + @Override + public void onAccountSyncableChanged(AccountItem accountItem) { + if (accountItem.isSyncable()) + addAccount(accountItem); + else + removeAccount(accountItem); + } - @Override - public void onAccountsUpdated(final Account[] accounts) { - Application.getInstance().runInBackground(new Runnable() { - @Override - public void run() { - HashSet existed = new HashSet(syncableAccounts); - String type = getAccountType(); - for (Account account : accounts) - if (type.equals(account.type)) - if (!existed.remove(account.name)) - LogManager.e(this, "Create account: " - + account.name); - disableSyncable(existed); - } - }); - } + @Override + public void onAccountsUpdated(final Account[] accounts) { + Application.getInstance().runInBackground(new Runnable() { + @Override + public void run() { + HashSet existed = new HashSet(syncableAccounts); + String type = getAccountType(); + for (Account account : accounts) + if (type.equals(account.type)) + if (!existed.remove(account.name)) + LogManager.e(this, "Create account: " + + account.name); + disableSyncable(existed); + } + }); + } - /** - * Disables synchronization based on removed system accounts. - * - * @param accounts - */ - private void disableSyncable(final Collection accounts) { - Application.getInstance().runOnUiThread(new Runnable() { - @Override - public void run() { - for (String account : accounts) { - LogManager.w(this, "Disable synchronization for: " - + account); - if (com.xabber.android.data.account.AccountManager - .getInstance().getAccount(account) != null) - com.xabber.android.data.account.AccountManager - .getInstance().setSyncable(account, false); - } - } - }); - } + /** + * Disables synchronization based on removed system accounts. + * + * @param accounts + */ + private void disableSyncable(final Collection accounts) { + Application.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + for (String account : accounts) { + LogManager.w(this, "Disable synchronization for: " + + account); + if (com.xabber.android.data.account.AccountManager + .getInstance().getAccount(account) != null) + com.xabber.android.data.account.AccountManager + .getInstance().setSyncable(account, false); + } + } + }); + } } diff --git a/app/src/main/java/com/xabber/android/data/roster/SystemContactStatus.java b/app/src/main/java/com/xabber/android/data/roster/SystemContactStatus.java index ea285a4571..b0a33c5fc7 100644 --- a/app/src/main/java/com/xabber/android/data/roster/SystemContactStatus.java +++ b/app/src/main/java/com/xabber/android/data/roster/SystemContactStatus.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -21,115 +21,114 @@ /** * Represents information about status of contact in system contact list. - * + * * @author alexander.ivanov - * */ class SystemContactStatus { - public static final SystemContactStatus UNAVAILABLE = new SystemContactStatus( - getPresence(StatusMode.unavailable), ""); - - /** - * Contact's presence level. - * - * @see {@link StatusUpdates#PRESENCE} - */ - private final Integer presence; - - /** - * Contact's status text. - */ - private final String text; - - public SystemContactStatus(Integer presence, String text) { - super(); - this.presence = presence; - this.text = text; - } - - private static Integer getPresence(StatusMode statusMode) { - if (statusMode == StatusMode.available) - return Im.AVAILABLE; - else if (statusMode == StatusMode.away) - return Im.AWAY; - else if (statusMode == StatusMode.chat) - return Im.AVAILABLE; - else if (statusMode == StatusMode.connection) - return Im.OFFLINE; - else if (statusMode == StatusMode.dnd) - return Im.DO_NOT_DISTURB; - else if (statusMode == StatusMode.invisible) - return Im.INVISIBLE; - else if (statusMode == StatusMode.unavailable) - return null; - else if (statusMode == StatusMode.unsubscribed) - return Im.OFFLINE; - else if (statusMode == StatusMode.xa) - return Im.IDLE; - else - return null; - } - - /** - * Create new status object from current contact`s status mode and text. - * - * @param rosterContact - * @return - */ - public static SystemContactStatus createStatus(RosterContact rosterContact) { - return new SystemContactStatus( - SystemContactStatus.getPresence(rosterContact.getStatusMode()), - rosterContact.getStatusText()); - } - - public boolean isEmpty() { - return presence == null && "".equals(text); - } - - public Integer getPresence() { - return presence; - } - - public String getText() { - return text; - } - - @Override - public String toString() { - return presence + ":" + text; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result - + ((presence == null) ? 0 : presence.hashCode()); - result = prime * result + ((text == null) ? 0 : text.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - SystemContactStatus other = (SystemContactStatus) obj; - if (presence == null) { - if (other.presence != null) - return false; - } else if (!presence.equals(other.presence)) - return false; - if (text == null) { - if (other.text != null) - return false; - } else if (!text.equals(other.text)) - return false; - return true; - } + public static final SystemContactStatus UNAVAILABLE = new SystemContactStatus( + getPresence(StatusMode.unavailable), ""); + + /** + * Contact's presence level. + * + * @see {@link StatusUpdates#PRESENCE} + */ + private final Integer presence; + + /** + * Contact's status text. + */ + private final String text; + + public SystemContactStatus(Integer presence, String text) { + super(); + this.presence = presence; + this.text = text; + } + + private static Integer getPresence(StatusMode statusMode) { + if (statusMode == StatusMode.available) + return Im.AVAILABLE; + else if (statusMode == StatusMode.away) + return Im.AWAY; + else if (statusMode == StatusMode.chat) + return Im.AVAILABLE; + else if (statusMode == StatusMode.connection) + return Im.OFFLINE; + else if (statusMode == StatusMode.dnd) + return Im.DO_NOT_DISTURB; + else if (statusMode == StatusMode.invisible) + return Im.INVISIBLE; + else if (statusMode == StatusMode.unavailable) + return null; + else if (statusMode == StatusMode.unsubscribed) + return Im.OFFLINE; + else if (statusMode == StatusMode.xa) + return Im.IDLE; + else + return null; + } + + /** + * Create new status object from current contact`s status mode and text. + * + * @param rosterContact + * @return + */ + public static SystemContactStatus createStatus(RosterContact rosterContact) { + return new SystemContactStatus( + SystemContactStatus.getPresence(rosterContact.getStatusMode()), + rosterContact.getStatusText()); + } + + public boolean isEmpty() { + return presence == null && "".equals(text); + } + + public Integer getPresence() { + return presence; + } + + public String getText() { + return text; + } + + @Override + public String toString() { + return presence + ":" + text; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((presence == null) ? 0 : presence.hashCode()); + result = prime * result + ((text == null) ? 0 : text.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + SystemContactStatus other = (SystemContactStatus) obj; + if (presence == null) { + if (other.presence != null) + return false; + } else if (!presence.equals(other.presence)) + return false; + if (text == null) { + if (other.text != null) + return false; + } else if (!text.equals(other.text)) + return false; + return true; + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/receiver/BootReceiver.java b/app/src/main/java/com/xabber/android/receiver/BootReceiver.java index e307d85a66..0cfdb7259d 100644 --- a/app/src/main/java/com/xabber/android/receiver/BootReceiver.java +++ b/app/src/main/java/com/xabber/android/receiver/BootReceiver.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -23,19 +23,18 @@ /** * Android boot receiver. - * + * * @author alexander.ivanov - * */ public class BootReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - if (SettingsManager.connectionStartAtBoot()) { - context.startService(XabberService.createIntent(context)); - } else { - android.os.Process.killProcess(android.os.Process.myPid()); - } - } + @Override + public void onReceive(Context context, Intent intent) { + if (SettingsManager.connectionStartAtBoot()) { + context.startService(XabberService.createIntent(context)); + } else { + android.os.Process.killProcess(android.os.Process.myPid()); + } + } } diff --git a/app/src/main/java/com/xabber/android/receiver/ComposingPausedReceiver.java b/app/src/main/java/com/xabber/android/receiver/ComposingPausedReceiver.java index f7b8959c6e..ab74af917c 100644 --- a/app/src/main/java/com/xabber/android/receiver/ComposingPausedReceiver.java +++ b/app/src/main/java/com/xabber/android/receiver/ComposingPausedReceiver.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -23,30 +23,28 @@ /** * Receiver for scheduled pause of composing. - * + * * @author alexander.ivanov - * */ public class ComposingPausedReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - ChatStateManager.getInstance().onPaused(intent, getAccount(intent), - getUser(intent)); - } - - public static Intent createIntent(Context context, String account, - String user) { - return new EntityIntentBuilder(context, ComposingPausedReceiver.class) - .setAccount(account).setUser(user).build(); - } - - private static String getAccount(Intent intent) { - return EntityIntentBuilder.getAccount(intent); - } - - private static String getUser(Intent intent) { - return EntityIntentBuilder.getUser(intent); - } + @Override + public void onReceive(Context context, Intent intent) { + ChatStateManager.getInstance().onPaused(getAccount(intent), getUser(intent)); + } + + public static Intent createIntent(Context context, String account, + String user) { + return new EntityIntentBuilder(context, ComposingPausedReceiver.class) + .setAccount(account).setUser(user).build(); + } + + private static String getAccount(Intent intent) { + return EntityIntentBuilder.getAccount(intent); + } + + private static String getUser(Intent intent) { + return EntityIntentBuilder.getUser(intent); + } } diff --git a/app/src/main/java/com/xabber/android/receiver/ConnectivityReceiver.java b/app/src/main/java/com/xabber/android/receiver/ConnectivityReceiver.java index f33f6171e6..c433631d9f 100644 --- a/app/src/main/java/com/xabber/android/receiver/ConnectivityReceiver.java +++ b/app/src/main/java/com/xabber/android/receiver/ConnectivityReceiver.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -25,23 +25,22 @@ /** * Receiver for network events. - * + * * @author alexander.ivanov - * */ public class ConnectivityReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - if (!ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) - return; - NetworkInfo networkInfo = (NetworkInfo) intent - .getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO); - if (networkInfo == null) { - LogManager.e(this, "NO INFO"); - return; - } - NetworkManager.getInstance().onNetworkChange(networkInfo); - } + @Override + public void onReceive(Context context, Intent intent) { + if (!ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) + return; + NetworkInfo networkInfo = intent + .getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO); + if (networkInfo == null) { + LogManager.e(this, "NO INFO"); + return; + } + NetworkManager.getInstance().onNetworkChange(networkInfo); + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/receiver/GoAwayReceiver.java b/app/src/main/java/com/xabber/android/receiver/GoAwayReceiver.java index 20ff05494a..64055253dd 100644 --- a/app/src/main/java/com/xabber/android/receiver/GoAwayReceiver.java +++ b/app/src/main/java/com/xabber/android/receiver/GoAwayReceiver.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -22,19 +22,18 @@ /** * Receiver for scheduled status change to "away". - * + * * @author alexander.ivanov - * */ public class GoAwayReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - AccountManager.getInstance().goAway(); - } + @Override + public void onReceive(Context context, Intent intent) { + AccountManager.getInstance().goAway(); + } - public static Intent createIntent(Context context) { - return new Intent(context, GoAwayReceiver.class); - } + public static Intent createIntent(Context context) { + return new Intent(context, GoAwayReceiver.class); + } } diff --git a/app/src/main/java/com/xabber/android/receiver/GoXaReceiver.java b/app/src/main/java/com/xabber/android/receiver/GoXaReceiver.java index 32a7b3ce05..b48d98a7b8 100644 --- a/app/src/main/java/com/xabber/android/receiver/GoXaReceiver.java +++ b/app/src/main/java/com/xabber/android/receiver/GoXaReceiver.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -22,19 +22,18 @@ /** * Receiver for scheduled status change to "eXtended Away". - * + * * @author alexander.ivanov - * */ public class GoXaReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - AccountManager.getInstance().goXa(); - } + @Override + public void onReceive(Context context, Intent intent) { + AccountManager.getInstance().goXa(); + } - public static Intent createIntent(Context context) { - return new Intent(context, GoXaReceiver.class); - } + public static Intent createIntent(Context context) { + return new Intent(context, GoXaReceiver.class); + } } diff --git a/app/src/main/java/com/xabber/android/receiver/ScreenReceiver.java b/app/src/main/java/com/xabber/android/receiver/ScreenReceiver.java index 200c14bb46..4051db6fbc 100644 --- a/app/src/main/java/com/xabber/android/receiver/ScreenReceiver.java +++ b/app/src/main/java/com/xabber/android/receiver/ScreenReceiver.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -22,15 +22,14 @@ /** * Receiver for screen on / off events. - * + * * @author alexander.ivanov - * */ public class ScreenReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - ScreenManager.getInstance().onScreen(intent); - } + @Override + public void onReceive(Context context, Intent intent) { + ScreenManager.getInstance().onScreen(intent); + } } diff --git a/app/src/main/java/com/xabber/android/receiver/ShutDownReceiver.java b/app/src/main/java/com/xabber/android/receiver/ShutDownReceiver.java new file mode 100644 index 0000000000..63be0ebbd9 --- /dev/null +++ b/app/src/main/java/com/xabber/android/receiver/ShutDownReceiver.java @@ -0,0 +1,14 @@ +package com.xabber.android.receiver; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +import com.xabber.android.data.Application; + +public class ShutDownReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + Application.getInstance().requestToClose(); + } +} diff --git a/app/src/main/java/com/xabber/android/service/AccountAuthenticatorService.java b/app/src/main/java/com/xabber/android/service/AccountAuthenticatorService.java index fbe68f56f0..2a2661e7b6 100644 --- a/app/src/main/java/com/xabber/android/service/AccountAuthenticatorService.java +++ b/app/src/main/java/com/xabber/android/service/AccountAuthenticatorService.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -22,19 +22,18 @@ /** * Service required for system contact list integration. - * + * * @author alexander.ivanov - * */ public class AccountAuthenticatorService extends Service { - @Override - public IBinder onBind(Intent intent) { - if (intent.getAction().equals( - android.accounts.AccountManager.ACTION_AUTHENTICATOR_INTENT)) - return AccountAuthenticator.getInstance().getIBinder(); - else - return null; - } + @Override + public IBinder onBind(Intent intent) { + if (intent.getAction().equals( + android.accounts.AccountManager.ACTION_AUTHENTICATOR_INTENT)) + return AccountAuthenticator.getInstance().getIBinder(); + else + return null; + } } diff --git a/app/src/main/java/com/xabber/android/service/SyncAdapterService.java b/app/src/main/java/com/xabber/android/service/SyncAdapterService.java index 9c3c744e62..ad88c8515e 100644 --- a/app/src/main/java/com/xabber/android/service/SyncAdapterService.java +++ b/app/src/main/java/com/xabber/android/service/SyncAdapterService.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -26,36 +26,35 @@ /** * Service required for system contact list integration. - * + * * @author alexander.ivanov - * */ public class SyncAdapterService extends Service { - private SyncAdapter syncAdapter; + private SyncAdapter syncAdapter; - @Override - public IBinder onBind(Intent intent) { - return syncAdapter.getSyncAdapterBinder(); - } + @Override + public IBinder onBind(Intent intent) { + return syncAdapter.getSyncAdapterBinder(); + } - @Override - public void onCreate() { - super.onCreate(); - syncAdapter = new SyncAdapter(getApplicationContext(), true); - } + @Override + public void onCreate() { + super.onCreate(); + syncAdapter = new SyncAdapter(getApplicationContext(), true); + } - public static class SyncAdapter extends AbstractThreadedSyncAdapter { + public static class SyncAdapter extends AbstractThreadedSyncAdapter { - public SyncAdapter(Context context, boolean autoInitialize) { - super(context, autoInitialize); - } + public SyncAdapter(Context context, boolean autoInitialize) { + super(context, autoInitialize); + } - @Override - public void onPerformSync(Account account, Bundle extras, - String authority, ContentProviderClient provider, - SyncResult syncResult) { - } - } + @Override + public void onPerformSync(Account account, Bundle extras, + String authority, ContentProviderClient provider, + SyncResult syncResult) { + } + } } diff --git a/app/src/main/java/com/xabber/android/service/XabberService.java b/app/src/main/java/com/xabber/android/service/XabberService.java index 892e2f1c1f..d6c1598ac3 100644 --- a/app/src/main/java/com/xabber/android/service/XabberService.java +++ b/app/src/main/java/com/xabber/android/service/XabberService.java @@ -1,23 +1,19 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.service; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -import android.app.Notification; import android.app.Service; import android.content.Context; import android.content.Intent; @@ -30,120 +26,56 @@ /** * Basic service to work in background. - * + * * @author alexander.ivanov - * */ public class XabberService extends Service { - private Method startForeground; - private Method stopForeground; - - private static XabberService instance; - - public static XabberService getInstance() { - return instance; - } - - @Override - public void onCreate() { - super.onCreate(); - instance = this; - LogManager.i(this, "onCreate"); - - // Try to get methods supported in API Level 5+ - try { - startForeground = getClass().getMethod("startForeground", - new Class[] { int.class, Notification.class }); - stopForeground = getClass().getMethod("stopForeground", - new Class[] { boolean.class }); - } catch (NoSuchMethodException e) { - startForeground = stopForeground = null; - } - - changeForeground(); - } - - public void changeForeground() { - if (SettingsManager.eventsPersistent() - && Application.getInstance().isInitialized()) - startForegroundWrapper(NotificationManager.getInstance() - .getPersistentNotification()); - else - stopForegroundWrapper(); - } - - @Override - public void onStart(Intent intent, int startId) { - super.onStart(intent, startId); - Application.getInstance().onServiceStarted(); - } - - @Override - public void onDestroy() { - super.onDestroy(); - LogManager.i(this, "onDestroy"); - stopForegroundWrapper(); - Application.getInstance().onServiceDestroy(); - } - - @Override - public IBinder onBind(Intent intent) { - return null; - } - - /** - * This is a wrapper around the new startForeground method, using the older - * APIs if it is not available. - */ - void startForegroundWrapper(Notification notification) { - if (startForeground != null) { - Object[] startForegroundArgs = new Object[] { - Integer.valueOf(NotificationManager.PERSISTENT_NOTIFICATION_ID), - notification }; - try { - startForeground.invoke(this, startForegroundArgs); - } catch (InvocationTargetException e) { - // Should not happen. - LogManager.w(this, "Unable to invoke startForeground" + e); - } catch (IllegalAccessException e) { - // Should not happen. - LogManager.w(this, "Unable to invoke startForeground" + e); - } - } else { - setForeground(true); - try { - ((android.app.NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)) - .notify(NotificationManager.PERSISTENT_NOTIFICATION_ID, - notification); - } catch (SecurityException e) { - } - } - } - - /** - * This is a wrapper around the new stopForeground method, using the older - * APIs if it is not available. - */ - void stopForegroundWrapper() { - if (stopForeground != null) { - try { - stopForeground.invoke(this, new Object[] { Boolean.TRUE }); - // We don't want to clear notification bar. - } catch (InvocationTargetException e) { - // Should not happen. - LogManager.w(this, "Unable to invoke stopForeground" + e); - } catch (IllegalAccessException e) { - // Should not happen. - LogManager.w(this, "Unable to invoke stopForeground" + e); - } - } else { - setForeground(false); - } - } - - public static Intent createIntent(Context context) { - return new Intent(context, XabberService.class); - } + private static XabberService instance; + + public static XabberService getInstance() { + return instance; + } + + @Override + public void onCreate() { + super.onCreate(); + instance = this; + LogManager.i(this, "onCreate"); + changeForeground(); + } + + public void changeForeground() { + if (SettingsManager.eventsPersistent() + && Application.getInstance().isInitialized()) + startForeground(NotificationManager.PERSISTENT_NOTIFICATION_ID, + NotificationManager.getInstance() + .getPersistentNotification()); + else + stopForeground(true); + } + + @Override + public void onStart(Intent intent, int startId) { + super.onStart(intent, startId); + Application.getInstance().onServiceStarted(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + LogManager.i(this, "onDestroy"); + stopForeground(true); + Application.getInstance().onServiceDestroy(); + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + public static Intent createIntent(Context context) { + return new Intent(context, XabberService.class); + } } diff --git a/app/src/main/java/com/xabber/android/ui/AboutViewer.java b/app/src/main/java/com/xabber/android/ui/AboutViewer.java index dac81738fa..e4a292ae31 100644 --- a/app/src/main/java/com/xabber/android/ui/AboutViewer.java +++ b/app/src/main/java/com/xabber/android/ui/AboutViewer.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + *

* This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + *

* Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + *

* You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,28 +16,103 @@ import android.content.Context; import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.net.Uri; import android.os.Bundle; +import android.support.design.widget.CollapsingToolbarLayout; +import android.support.v4.app.NavUtils; +import android.support.v7.widget.Toolbar; import android.text.method.LinkMovementMethod; +import android.view.View; +import android.widget.ImageView; import android.widget.TextView; +import android.widget.Toast; +import com.bumptech.glide.Glide; +import com.xabber.android.R; import com.xabber.android.ui.helper.ManagedActivity; -import com.xabber.androiddev.R; -public class AboutViewer extends ManagedActivity { +public class AboutViewer extends ManagedActivity implements View.OnClickListener { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.about_viewer); - ((TextView) findViewById(R.id.about_version)) - .setText(getString(R.string.about_version, - getString(R.string.application_version))); - ((TextView) findViewById(R.id.about_license)) - .setMovementMethod(LinkMovementMethod.getInstance()); - } + public static Intent createIntent(Context context) { + return new Intent(context, AboutViewer.class); + } - public static Intent createIntent(Context context) { - return new Intent(context, AboutViewer.class); - } + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.about_viewer); + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_default); + toolbar.setNavigationIcon(R.drawable.ic_arrow_left_white_24dp); + toolbar.setNavigationOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + NavUtils.navigateUpFromSameTask(AboutViewer.this); + } + }); + findViewById(R.id.about_github).setOnClickListener(this); + findViewById(R.id.about_twitter).setOnClickListener(this); + findViewById(R.id.about_redsolution).setOnClickListener(this); + findViewById(R.id.about_text_xmpp_protocol).setOnClickListener(this); + + ((TextView) findViewById(R.id.about_text_developers)) + .setMovementMethod(LinkMovementMethod.getInstance()); + ((TextView) findViewById(R.id.about_text_translators)) + .setMovementMethod(LinkMovementMethod.getInstance()); + ((TextView) findViewById(R.id.about_text_license)) + .setMovementMethod(LinkMovementMethod.getInstance()); + + CollapsingToolbarLayout collapsingToolbar = + (CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar); + collapsingToolbar.setTitle(getString(R.string.application_title_short)); + + ((TextView) findViewById(R.id.about_version)).setText(getVersionName()); + + loadBackdrop(); + } + + private void loadBackdrop() { + final ImageView imageView = (ImageView) findViewById(R.id.backdrop); + Glide.with(this).load(R.drawable.about_backdrop).centerCrop().into(imageView); + } + + private String getVersionName() { + try { + PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0); + return getString(R.string.application_title_full) + " " + pInfo.versionName; + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + return ""; + } + + @Override + public void onClick(View v) { + + switch (v.getId()) { + case R.id.about_redsolution: + sendUrlViewIntent(getString(R.string.about_redsolution_url)); + break; + + case R.id.about_github: + sendUrlViewIntent(getString(R.string.about_xabber_github_url)); + break; + + case R.id.about_twitter: + sendUrlViewIntent(getString(R.string.about_xabber_twitter_url)); + break; + + case R.id.about_text_xmpp_protocol: + Toast.makeText(this, R.string.about_shameless_quote_from_wiki, Toast.LENGTH_SHORT).show(); + break; + } + } + + private void sendUrlViewIntent(String url) { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse(url)); + startActivity(i); + } } diff --git a/app/src/main/java/com/xabber/android/ui/AccountAdd.java b/app/src/main/java/com/xabber/android/ui/AccountAdd.java index 2d021b6dc7..06d4b31427 100644 --- a/app/src/main/java/com/xabber/android/ui/AccountAdd.java +++ b/app/src/main/java/com/xabber/android/ui/AccountAdd.java @@ -1,216 +1,84 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.ui; -import android.app.Dialog; import android.content.Context; import android.content.Intent; import android.os.Bundle; -import android.view.View; -import android.view.inputmethod.InputMethodManager; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemSelectedListener; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.EditText; -import android.widget.Spinner; -import android.widget.TextView; - -import com.xabber.android.data.Application; -import com.xabber.android.data.NetworkException; -import com.xabber.android.data.account.AccountManager; -import com.xabber.android.data.account.AccountType; +import android.support.v7.widget.Toolbar; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; + +import com.xabber.android.R; import com.xabber.android.data.intent.AccountIntentBuilder; -import com.xabber.android.ui.adapter.AccountTypeAdapter; -import com.xabber.android.ui.dialog.OrbotInstallerDialogBuilder; +import com.xabber.android.ui.helper.BarPainter; import com.xabber.android.ui.helper.ManagedActivity; -import com.xabber.android.ui.helper.OrbotHelper; -import com.xabber.androiddev.R; - -public class AccountAdd extends ManagedActivity implements - View.OnClickListener, OnItemSelectedListener { - - private static final String SAVED_ACCOUNT_TYPE = "com.xabber.android.ui.AccountAdd.ACCOUNT_TYPE"; - - private static final int OAUTH_WML_REQUEST_CODE = 1; - - private static final int ORBOT_DIALOG_ID = 9050; - - private CheckBox storePasswordView; - private CheckBox useOrbotView; - private CheckBox syncableView; - private Spinner accountTypeView; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (isFinishing()) - return; - - setContentView(R.layout.account_add); - - storePasswordView = (CheckBox) findViewById(R.id.store_password); - useOrbotView = (CheckBox) findViewById(R.id.use_orbot); - syncableView = (CheckBox) findViewById(R.id.syncable); - if (!Application.getInstance().isContactsSupported()) { - syncableView.setVisibility(View.GONE); - syncableView.setChecked(false); - } - - accountTypeView = (Spinner) findViewById(R.id.account_type); - accountTypeView.setAdapter(new AccountTypeAdapter(this)); - accountTypeView.setOnItemSelectedListener(this); - - String accountType; - if (savedInstanceState == null) - accountType = null; - else - accountType = savedInstanceState.getString(SAVED_ACCOUNT_TYPE); - accountTypeView.setSelection(0); - for (int position = 0; position < accountTypeView.getCount(); position++) - if (((AccountType) accountTypeView.getItemAtPosition(position)) - .getName().equals(accountType)) { - accountTypeView.setSelection(position); - break; - } - - ((Button) findViewById(R.id.ok)).setOnClickListener(this); - InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - inputManager.hideSoftInputFromWindow(findViewById(R.id.ok) - .getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putString(SAVED_ACCOUNT_TYPE, - ((AccountType) accountTypeView.getSelectedItem()).getName()); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (requestCode == OAUTH_WML_REQUEST_CODE) { - if (resultCode == RESULT_OK && !OAuthActivity.isInvalidated(data)) { - String token = OAuthActivity.getToken(data); - if (token == null) { - Application.getInstance().onError( - R.string.AUTHENTICATION_FAILED); - } else { - String account; - try { - account = AccountManager.getInstance() - .addAccount( - null, - token, - (AccountType) accountTypeView - .getSelectedItem(), - syncableView.isChecked(), - storePasswordView.isChecked(), - useOrbotView.isChecked()); - } catch (NetworkException e) { - Application.getInstance().onError(e); - return; - } - setResult(RESULT_OK, - createAuthenticatorResult(this, account)); - finish(); - } - } - } - } - - @Override - public void onClick(View view) { - switch (view.getId()) { - case R.id.ok: - if (useOrbotView.isChecked() && !OrbotHelper.isOrbotInstalled()) { - showDialog(ORBOT_DIALOG_ID); - return; - } - AccountType accountType = (AccountType) accountTypeView - .getSelectedItem(); - if (accountType.getProtocol().isOAuth()) { - startActivityForResult( - OAuthActivity.createIntent(this, - accountType.getProtocol()), - OAUTH_WML_REQUEST_CODE); - } else { - EditText userView = (EditText) findViewById(R.id.account_user_name); - EditText passwordView = (EditText) findViewById(R.id.account_password); - String account; - try { - account = AccountManager.getInstance().addAccount( - userView.getText().toString(), - passwordView.getText().toString(), accountType, - syncableView.isChecked(), - storePasswordView.isChecked(), - useOrbotView.isChecked()); - } catch (NetworkException e) { - Application.getInstance().onError(e); - return; - } - setResult(RESULT_OK, createAuthenticatorResult(this, account)); - finish(); - } - break; - default: - break; - } - } - - @Override - public void onItemSelected(AdapterView adapterView, View view, - int position, long id) { - AccountType accountType = (AccountType) accountTypeView - .getSelectedItem(); - if (accountType.getProtocol().isOAuth()) - findViewById(R.id.auth_panel).setVisibility(View.GONE); - else - findViewById(R.id.auth_panel).setVisibility(View.VISIBLE); - ((TextView) findViewById(R.id.account_user_name)).setHint(accountType - .getHint()); - ((TextView) findViewById(R.id.account_help)).setText(accountType - .getHelp()); - } - - @Override - public void onNothingSelected(AdapterView adapterView) { - accountTypeView.setSelection(0); - } - - @Override - protected Dialog onCreateDialog(int id) { - if (id == ORBOT_DIALOG_ID) { - return new OrbotInstallerDialogBuilder(this, ORBOT_DIALOG_ID) - .create(); - } - return super.onCreateDialog(id); - } - - public static Intent createIntent(Context context) { - return new Intent(context, AccountAdd.class); - } - - private static Intent createAuthenticatorResult(Context context, - String account) { - return new AccountIntentBuilder(null, null).setAccount(account).build(); - } - - public static String getAuthenticatorResultAccount(Intent intent) { - return AccountIntentBuilder.getAccount(intent); - } +public class AccountAdd extends ManagedActivity { + + public static Intent createIntent(Context context) { + return new Intent(context, AccountAdd.class); + } + + public static Intent createAuthenticatorResult(String account) { + return new AccountIntentBuilder(null, null).setAccount(account).build(); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (isFinishing()) + return; + + setContentView(R.layout.activity_with_toolbar_and_container); + + if (savedInstanceState == null) { + getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, AccountAddFragment.newInstance()).commit(); + } + + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_default); + + setSupportActionBar(toolbar); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_clear_white_24dp); + getSupportActionBar().setTitle(null); + + BarPainter barPainter = new BarPainter(this, toolbar); + barPainter.setDefaultColor(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater=getMenuInflater(); + inflater.inflate(R.menu.add_account, menu); + menu.findItem(R.id.action_add_account).setIcon(null); + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_add_account: + ((AccountAddFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_container)).addAccount(); + + return true; + + default: + return super.onOptionsItemSelected(item); + } + } } diff --git a/app/src/main/java/com/xabber/android/ui/AccountAddFragment.java b/app/src/main/java/com/xabber/android/ui/AccountAddFragment.java new file mode 100644 index 0000000000..9396487fdc --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/AccountAddFragment.java @@ -0,0 +1,152 @@ +package com.xabber.android.ui; + +import android.app.Activity; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.Spinner; +import android.widget.TextView; +import android.widget.Toast; + +import com.xabber.android.R; +import com.xabber.android.data.Application; +import com.xabber.android.data.NetworkException; +import com.xabber.android.data.account.AccountManager; +import com.xabber.android.data.account.AccountType; +import com.xabber.android.ui.adapter.AccountTypeAdapter; +import com.xabber.android.ui.dialog.OrbotInstallerDialogBuilder; +import com.xabber.android.ui.helper.OrbotHelper; +import com.xabber.android.ui.preferences.AccountEditor; + +public class AccountAddFragment extends Fragment implements View.OnClickListener, AdapterView.OnItemSelectedListener { + + private static final String SAVED_ACCOUNT_TYPE = "com.xabber.android.ui.AccountAdd.ACCOUNT_TYPE"; + private CheckBox storePasswordView; + private CheckBox useOrbotView; + private CheckBox createAccountCheckBox; + private Spinner accountTypeView; + private LinearLayout passwordConfirmView; + private EditText userView; + private EditText passwordView; + private EditText passwordConfirmEditText; + private View authPanel; + private TextView accountHelpView; + + public static AccountAddFragment newInstance() { + return new AccountAddFragment(); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.account_add_fragment, container, false); + + accountTypeView = (Spinner) view.findViewById(R.id.account_type); + accountTypeView.setAdapter(new AccountTypeAdapter(getActivity())); + accountTypeView.setOnItemSelectedListener(this); + + String accountType; + if (savedInstanceState == null) { + accountType = null; + } else { + accountType = savedInstanceState.getString(SAVED_ACCOUNT_TYPE); + } + + accountTypeView.setSelection(0); + for (int position = 0; position < accountTypeView.getCount(); position++) { + if (((AccountType) accountTypeView.getItemAtPosition(position)).getName().equals(accountType)){ + accountTypeView.setSelection(position); + break; + } + } + + storePasswordView = (CheckBox) view.findViewById(R.id.store_password); + useOrbotView = (CheckBox) view.findViewById(R.id.use_orbot); + createAccountCheckBox = (CheckBox) view.findViewById(R.id.register_account); + createAccountCheckBox.setOnClickListener(this); + + authPanel = view.findViewById(R.id.auth_panel); + + userView = (EditText) view.findViewById(R.id.account_user_name); + accountHelpView = (TextView) view.findViewById(R.id.account_help); + passwordView = (EditText) view.findViewById(R.id.account_password); + passwordConfirmEditText = (EditText) view.findViewById(R.id.confirm_password); + + passwordConfirmView = (LinearLayout) view.findViewById(R.id.confirm_password_layout); + + return view; + } + + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putString(SAVED_ACCOUNT_TYPE, ((AccountType) accountTypeView.getSelectedItem()).getName()); + } + + @Override + public void onClick(View v) { + switch (v.getId()) { + case R.id.register_account: + if(createAccountCheckBox.isChecked()) { + passwordConfirmView.setVisibility(View.VISIBLE); + } else { + passwordConfirmView.setVisibility(View.GONE); + } + default: + break; + } + } + + public void addAccount() { + if (useOrbotView.isChecked() && !OrbotHelper.isOrbotInstalled()) { + OrbotInstallerDialogBuilder.show(getActivity()); + return; + } + + if (createAccountCheckBox.isChecked() && + !passwordView.getText().toString().contentEquals(passwordConfirmEditText.getText().toString())) { + Toast.makeText(getActivity(), getString(R.string.CONFIRM_PASSWORD), Toast.LENGTH_LONG).show(); + return; + } + + AccountType accountType = (AccountType) accountTypeView.getSelectedItem(); + + String account; + try { + account = AccountManager.getInstance().addAccount( + userView.getText().toString(), + passwordView.getText().toString(), accountType, + false, + storePasswordView.isChecked(), + useOrbotView.isChecked(), + createAccountCheckBox.isChecked()); + } catch (NetworkException e) { + Application.getInstance().onError(e); + return; + } + + getActivity().setResult(Activity.RESULT_OK, AccountAdd.createAuthenticatorResult(account)); + startActivity(AccountEditor.createIntent(getActivity(), account)); + getActivity().finish(); + } + + + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + AccountType accountType = (AccountType) accountTypeView.getSelectedItem(); + authPanel.setVisibility(View.VISIBLE); + accountHelpView.setText(accountType.getHelp()); + userView.setHint(accountType.getHint()); + } + + @Override + public void onNothingSelected(AdapterView parent) { + accountTypeView.setSelection(0); + } +} diff --git a/app/src/main/java/com/xabber/android/ui/AccountEditor.java b/app/src/main/java/com/xabber/android/ui/AccountEditor.java deleted file mode 100644 index 22b73c5314..0000000000 --- a/app/src/main/java/com/xabber/android/ui/AccountEditor.java +++ /dev/null @@ -1,302 +0,0 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.xabber.android.ui; - -import java.util.HashMap; -import java.util.Map; - -import android.app.Dialog; -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.preference.Preference; -import android.preference.Preference.OnPreferenceClickListener; -import android.widget.Toast; - -import com.xabber.android.data.Application; -import com.xabber.android.data.account.AccountItem; -import com.xabber.android.data.account.AccountManager; -import com.xabber.android.data.account.AccountProtocol; -import com.xabber.android.data.account.ArchiveMode; -import com.xabber.android.data.connection.ProxyType; -import com.xabber.android.data.connection.TLSMode; -import com.xabber.android.data.intent.AccountIntentBuilder; -import com.xabber.android.ui.dialog.OrbotInstallerDialogBuilder; -import com.xabber.android.ui.helper.BaseSettingsActivity; -import com.xabber.android.ui.helper.OrbotHelper; -import com.xabber.androiddev.R; - -public class AccountEditor extends BaseSettingsActivity implements - OnPreferenceClickListener { - - private static final int OAUTH_WML_REQUEST_CODE = 1; - - private static final String SAVED_TOKEN = "com.xabber.android.ui.AccountEditor.TOKEN"; - - private static final String INVALIDATED_TOKEN = "com.xabber.android.ui.AccountEditor.INVALIDATED"; - - private static final int ORBOT_DIALOG_ID = 9050; - - private String account; - private AccountItem accountItem; - - private String token; - - private Preference oauthPreference; - - @Override - protected void onInflate(Bundle savedInstanceState) { - account = AccountEditor.getAccount(getIntent()); - if (account == null) { - finish(); - return; - } - accountItem = AccountManager.getInstance().getAccount(account); - if (accountItem == null) { - Application.getInstance().onError(R.string.NO_SUCH_ACCOUNT); - finish(); - return; - } - AccountProtocol protocol = accountItem.getConnectionSettings() - .getProtocol(); - if (protocol == AccountProtocol.xmpp) - addPreferencesFromResource(R.xml.account_editor_xmpp); - else if (protocol == AccountProtocol.gtalk) - addPreferencesFromResource(R.xml.account_editor_xmpp); - else if (protocol == AccountProtocol.wlm) - addPreferencesFromResource(R.xml.account_editor_oauth); - else - throw new IllegalStateException(); - if (!Application.getInstance().isContactsSupported()) - getPreferenceScreen().removePreference( - findPreference(getString(R.string.account_syncable_key))); - setTitle(getString(R.string.account_editor_title, - getString(protocol.getShortResource()), AccountManager - .getInstance().getVerboseName(account))); - if (savedInstanceState == null) - token = accountItem.getConnectionSettings().getPassword(); - else - token = savedInstanceState.getString(SAVED_TOKEN); - oauthPreference = findPreference(getString(R.string.account_oauth_key)); - if (oauthPreference != null) - oauthPreference.setOnPreferenceClickListener(this); - onOAuthChange(); - AccountManager.getInstance().removeAuthorizationError(account); - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putString(SAVED_TOKEN, token); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (requestCode == OAUTH_WML_REQUEST_CODE) { - if (resultCode == RESULT_OK) { - if (OAuthActivity.isInvalidated(data)) { - token = INVALIDATED_TOKEN; - } else { - String value = OAuthActivity.getToken(data); - if (value == null) - Application.getInstance().onError( - R.string.AUTHENTICATION_FAILED); - else - token = value; - } - onOAuthChange(); - } - } - } - - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - if (getString(R.string.account_port_key).equals(preference.getKey())) - try { - Integer.parseInt((String) newValue); - } catch (NumberFormatException e) { - Toast.makeText(this, getString(R.string.account_invalid_port), - Toast.LENGTH_LONG).show(); - return false; - } - if (getString(R.string.account_tls_mode_key) - .equals(preference.getKey()) - || getString(R.string.account_archive_mode_key).equals( - preference.getKey()) - || getString(R.string.account_proxy_type_key).equals( - preference.getKey())) - preference.setSummary((String) newValue); - else if (!getString(R.string.account_password_key).equals( - preference.getKey()) - && !getString(R.string.account_proxy_password_key).equals( - preference.getKey()) - && !getString(R.string.account_priority_key).equals( - preference.getKey())) - super.onPreferenceChange(preference, newValue); - if (getString(R.string.account_proxy_type_key).equals( - preference.getKey())) { - boolean enabled = !getString(R.string.account_proxy_type_none) - .equals(newValue) - && !getString(R.string.account_proxy_type_orbot).equals( - newValue); - for (int id : new Integer[] { R.string.account_proxy_host_key, - R.string.account_proxy_port_key, - R.string.account_proxy_user_key, - R.string.account_proxy_password_key, }) { - Preference proxyPreference = findPreference(getString(id)); - if (proxyPreference != null) - proxyPreference.setEnabled(enabled); - } - } - return true; - } - - private void onOAuthChange() { - if (oauthPreference == null) - return; - if (INVALIDATED_TOKEN.equals(token)) - oauthPreference.setSummary(R.string.account_oauth_invalidated); - else - oauthPreference.setSummary(R.string.account_oauth_summary); - } - - @Override - public boolean onPreferenceClick(Preference preference) { - if (getString(R.string.account_oauth_key).equals(preference.getKey())) { - startActivityForResult(OAuthActivity.createIntent(this, accountItem - .getConnectionSettings().getProtocol()), - OAUTH_WML_REQUEST_CODE); - return true; - } - return false; - } - - @Override - protected Map getValues() { - Map source = new HashMap(); - putValue(source, R.string.account_custom_key, accountItem - .getConnectionSettings().isCustom()); - putValue(source, R.string.account_host_key, accountItem - .getConnectionSettings().getHost()); - putValue(source, R.string.account_port_key, accountItem - .getConnectionSettings().getPort()); - putValue(source, R.string.account_server_key, accountItem - .getConnectionSettings().getServerName()); - putValue(source, R.string.account_username_key, accountItem - .getConnectionSettings().getUserName()); - putValue(source, R.string.account_store_password_key, - accountItem.isStorePassword()); - putValue(source, R.string.account_password_key, accountItem - .getConnectionSettings().getPassword()); - putValue(source, R.string.account_resource_key, accountItem - .getConnectionSettings().getResource()); - putValue(source, R.string.account_priority_key, - accountItem.getPriority()); - putValue(source, R.string.account_enabled_key, accountItem.isEnabled()); - putValue(source, R.string.account_sasl_key, accountItem - .getConnectionSettings().isSaslEnabled()); - putValue( - source, - R.string.account_tls_mode_key, - Integer.valueOf(accountItem.getConnectionSettings() - .getTlsMode().ordinal())); - putValue(source, R.string.account_compression_key, accountItem - .getConnectionSettings().useCompression()); - putValue( - source, - R.string.account_proxy_type_key, - Integer.valueOf(accountItem.getConnectionSettings() - .getProxyType().ordinal())); - putValue(source, R.string.account_proxy_host_key, accountItem - .getConnectionSettings().getProxyHost()); - putValue(source, R.string.account_proxy_port_key, accountItem - .getConnectionSettings().getProxyPort()); - putValue(source, R.string.account_proxy_user_key, accountItem - .getConnectionSettings().getProxyUser()); - putValue(source, R.string.account_proxy_password_key, accountItem - .getConnectionSettings().getProxyPassword()); - putValue(source, R.string.account_syncable_key, - accountItem.isSyncable()); - putValue(source, R.string.account_archive_mode_key, - Integer.valueOf(accountItem.getArchiveMode().ordinal())); - return source; - } - - @Override - protected Map getPreferences(Map source) { - Map result = super.getPreferences(source); - if (oauthPreference != null) - putValue(result, R.string.account_password_key, token); - return result; - } - - @Override - protected boolean setValues(Map source, - Map result) { - ProxyType proxyType = ProxyType.values()[getInt(result, - R.string.account_proxy_type_key)]; - if (proxyType == ProxyType.orbot && !OrbotHelper.isOrbotInstalled()) { - showDialog(ORBOT_DIALOG_ID); - return false; - } - AccountManager - .getInstance() - .updateAccount( - account, - getBoolean(result, R.string.account_custom_key), - getString(result, R.string.account_host_key), - getInt(result, R.string.account_port_key), - getString(result, R.string.account_server_key), - getString(result, R.string.account_username_key), - getBoolean(result, R.string.account_store_password_key), - getString(result, R.string.account_password_key), - getString(result, R.string.account_resource_key), - getInt(result, R.string.account_priority_key), - getBoolean(result, R.string.account_enabled_key), - getBoolean(result, R.string.account_sasl_key), - TLSMode.values()[getInt(result, - R.string.account_tls_mode_key)], - getBoolean(result, R.string.account_compression_key), - proxyType, - getString(result, R.string.account_proxy_host_key), - getInt(result, R.string.account_proxy_port_key), - getString(result, R.string.account_proxy_user_key), - getString(result, R.string.account_proxy_password_key), - getBoolean(result, R.string.account_syncable_key), - ArchiveMode.values()[getInt(result, - R.string.account_archive_mode_key)]); - return true; - } - - @Override - protected Dialog onCreateDialog(int id) { - if (id == ORBOT_DIALOG_ID) { - return new OrbotInstallerDialogBuilder(this, ORBOT_DIALOG_ID) - .create(); - } - return super.onCreateDialog(id); - } - - private static String getAccount(Intent intent) { - return AccountIntentBuilder.getAccount(intent); - } - - public static Intent createIntent(Context context, String account) { - return new AccountIntentBuilder(context, AccountEditor.class) - .setAccount(account).build(); - } - -} diff --git a/app/src/main/java/com/xabber/android/ui/AccountList.java b/app/src/main/java/com/xabber/android/ui/AccountList.java deleted file mode 100644 index 57257af336..0000000000 --- a/app/src/main/java/com/xabber/android/ui/AccountList.java +++ /dev/null @@ -1,139 +0,0 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.xabber.android.ui; - -import java.util.Collection; - -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.view.ContextMenu; -import android.view.MenuItem; - -import com.xabber.android.data.Application; -import com.xabber.android.data.account.AccountItem; -import com.xabber.android.data.account.AccountManager; -import com.xabber.android.data.account.OnAccountChangedListener; -import com.xabber.android.ui.adapter.AccountListAdapter; -import com.xabber.android.ui.adapter.BaseListEditorAdapter; -import com.xabber.android.ui.adapter.UpdatableAdapter; -import com.xabber.android.ui.helper.BaseListEditor; -import com.xabber.androiddev.R; - -public class AccountList extends BaseListEditor implements - OnAccountChangedListener { - - private static final int CONTEXT_MENU_VIEW_ACCOUNT_ID = 0x20; - private static final int CONTEXT_MENU_STATUS_EDITOR_ID = 0x30; - - @Override - protected int getAddTextResourceId() { - return R.string.account_add; - } - - @Override - protected Intent getAddIntent() { - return AccountAdd.createIntent(this); - } - - @Override - protected Intent getEditIntent(String actionWith) { - return AccountEditor.createIntent(this, actionWith); - } - - @Override - protected int getRemoveTextResourceId() { - return R.string.account_delete; - } - - @Override - protected String getRemoveConfirmation(String actionWith) { - return getString(R.string.account_delete_confirm, AccountManager - .getInstance().getVerboseName(actionWith)); - } - - @Override - protected void removeItem(String actionWith) { - AccountManager.getInstance().removeAccount(actionWith); - } - - @Override - protected BaseListEditorAdapter createListAdapter() { - return new AccountListAdapter(this); - } - - @Override - protected void onResume() { - super.onResume(); - Application.getInstance().addUIListener(OnAccountChangedListener.class, - this); - } - - @Override - protected void onPause() { - super.onPause(); - Application.getInstance().removeUIListener( - OnAccountChangedListener.class, this); - } - - @Override - protected void onCreateContextMenu(ContextMenu menu, String actionWith) { - final AccountItem accountItem = AccountManager.getInstance() - .getAccount(actionWith); - menu.setHeaderTitle(AccountManager.getInstance().getVerboseName( - actionWith)); - if (accountItem.isEnabled()) { - menu.add(0, CONTEXT_MENU_STATUS_EDITOR_ID, 0, getResources() - .getText(R.string.status_editor)); - } - menu.add(0, CONTEXT_MENU_VIEW_ACCOUNT_ID, 0, - getString(R.string.account_editor)); - super.onCreateContextMenu(menu, actionWith); - } - - @Override - public boolean onContextItemSelected(MenuItem item) { - if (super.onContextItemSelected(item)) - return true; - if (item.getItemId() == CONTEXT_MENU_VIEW_ACCOUNT_ID) { - startActivity(getEditIntent(getActionWith())); - return true; - } else if (item.getItemId() == CONTEXT_MENU_STATUS_EDITOR_ID) { - startActivity(StatusEditor.createIntent(this, getActionWith())); - return true; - } - return false; - } - - @Override - public void onAccountsChanged(Collection accounts) { - ((UpdatableAdapter) getListAdapter()).onChange(); - } - - @Override - protected String getSavedValue(Bundle bundle, String key) { - return bundle.getString(key); - } - - @Override - protected void putSavedValue(Bundle bundle, String key, String actionWith) { - bundle.putString(key, actionWith); - } - - public static Intent createIntent(Context context) { - return new Intent(context, AccountList.class); - } - -} diff --git a/app/src/main/java/com/xabber/android/ui/ArchiveRequest.java b/app/src/main/java/com/xabber/android/ui/ArchiveRequest.java index 9a37461bcf..029407168a 100644 --- a/app/src/main/java/com/xabber/android/ui/ArchiveRequest.java +++ b/app/src/main/java/com/xabber/android/ui/ArchiveRequest.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -19,58 +19,57 @@ import android.os.Bundle; import android.view.View; +import com.xabber.android.R; import com.xabber.android.data.Application; import com.xabber.android.data.account.AccountManager; import com.xabber.android.data.account.ArchiveMode; import com.xabber.android.data.intent.AccountIntentBuilder; import com.xabber.android.ui.helper.ManagedDialog; -import com.xabber.androiddev.R; /** * Dialog with request to enable message archive. - * + * * @author alexander.ivanov - * */ public class ArchiveRequest extends ManagedDialog { - private String account; + private String account; - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - findViewById(android.R.id.button3).setVisibility(View.GONE); - setDialogMessage(R.string.archive_available_request_message); - account = getAccount(getIntent()); - if (AccountManager.getInstance().getAccount(account) == null) { - Application.getInstance().onError(R.string.NO_SUCH_ACCOUNT); - finish(); - return; - } - } + public static Intent createIntent(Context context, String account) { + return new AccountIntentBuilder(context, ArchiveRequest.class) + .setAccount(account).build(); + } - @Override - public void onAccept() { - super.onAccept(); - AccountManager.getInstance() - .setArchiveMode(account, ArchiveMode.server); - finish(); - } + private static String getAccount(Intent intent) { + return AccountIntentBuilder.getAccount(intent); + } - @Override - public void onDecline() { - super.onDecline(); - AccountManager.getInstance().setArchiveMode(account, ArchiveMode.local); - finish(); - } + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + findViewById(android.R.id.button3).setVisibility(View.GONE); + setDialogMessage(R.string.archive_available_request_message); + account = getAccount(getIntent()); + if (AccountManager.getInstance().getAccount(account) == null) { + Application.getInstance().onError(R.string.NO_SUCH_ACCOUNT); + finish(); + return; + } + } - public static Intent createIntent(Context context, String account) { - return new AccountIntentBuilder(context, ArchiveRequest.class) - .setAccount(account).build(); - } + @Override + public void onAccept() { + super.onAccept(); + AccountManager.getInstance() + .setArchiveMode(account, ArchiveMode.server); + finish(); + } - private static String getAccount(Intent intent) { - return AccountIntentBuilder.getAccount(intent); - } + @Override + public void onDecline() { + super.onDecline(); + AccountManager.getInstance().setArchiveMode(account, ArchiveMode.local); + finish(); + } } diff --git a/app/src/main/java/com/xabber/android/ui/CertificateConfirmation.java b/app/src/main/java/com/xabber/android/ui/CertificateConfirmation.java index 580e11e568..baf87dc8a1 100644 --- a/app/src/main/java/com/xabber/android/ui/CertificateConfirmation.java +++ b/app/src/main/java/com/xabber/android/ui/CertificateConfirmation.java @@ -1,21 +1,19 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.ui; -import java.util.NoSuchElementException; - import android.content.Context; import android.content.Intent; import android.os.Bundle; @@ -23,129 +21,130 @@ import android.widget.Button; import android.widget.TextView; +import com.xabber.android.R; import com.xabber.android.data.connection.CertificateInvalidReason; import com.xabber.android.data.connection.CertificateManager; import com.xabber.android.data.connection.ConnectionManager; import com.xabber.android.data.connection.PendingCertificate; import com.xabber.android.data.intent.SegmentIntentBuilder; import com.xabber.android.ui.helper.ManagedDialog; -import com.xabber.androiddev.R; + +import java.util.NoSuchElementException; /** * Dialog to confirm invalid certificate. - * + * * @author alexander.ivanov - * */ public class CertificateConfirmation extends ManagedDialog { - private static final String SAVED_SHOW_DETAILS = "com.xabber.android.ui.CertificateConfirmation.SHOW_DETAILS"; - - private PendingCertificate pendingCertificate; - private boolean showDetails; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - String fingerprint = getFingerprint(getIntent()); - CertificateInvalidReason reason = getReason(getIntent()); - pendingCertificate = null; - if (fingerprint != null && reason != null) - pendingCertificate = CertificateManager.getInstance() - .getPendingCertificate(fingerprint, reason); - if (pendingCertificate == null) - finish(); - if (savedInstanceState == null) { - showDetails = false; - } else { - showDetails = savedInstanceState.getBoolean(SAVED_SHOW_DETAILS, - false); - } - ((Button) findViewById(android.R.id.button3)) - .setText(R.string.certificate_show_details); - setDialogTitle(R.string.INVALID_CERTIFICATE); - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putBoolean(SAVED_SHOW_DETAILS, showDetails); - } - - @Override - protected void onResume() { - super.onResume(); - update(); - } - - private void update() { - String reason = getString(pendingCertificate.getReason() - .getResourceId()); - String message = getString(R.string.certificate_confirmation, reason, - CertificateManager.showFingerprint(pendingCertificate - .getFingerprint())); - if (showDetails) { - String details = getString(R.string.certificate_details, - pendingCertificate.getIssuerCommonName(), - pendingCertificate.getIssuerOrganization(), - pendingCertificate.getIssuerOrganizationlUnit(), - pendingCertificate.getSerialNumber(), - pendingCertificate.getSubjectCommonName(), - pendingCertificate.getSubjectOrganization(), - pendingCertificate.getSubjectOrganizationlUnit(), - pendingCertificate.issuedOn(), - pendingCertificate.expiresOn()); - message += details; - findViewById(android.R.id.button3).setVisibility(View.GONE); - } - ((TextView) findViewById(android.R.id.message)).setText(message); - } - - @Override - public void onAccept() { - super.onAccept(); - CertificateManager.getInstance().accept( - pendingCertificate.getFingerprint(), - pendingCertificate.getReason()); - ConnectionManager.getInstance().updateConnections(true); - finish(); - } - - @Override - public void onDecline() { - super.onDecline(); - CertificateManager.getInstance().discard( - pendingCertificate.getFingerprint(), - pendingCertificate.getReason()); - finish(); - } - - @Override - public void onCenter() { - super.onCenter(); - showDetails = true; - update(); - } - - public static Intent createIntent(Context context, String fingerPrint, - CertificateInvalidReason reason) { - return new SegmentIntentBuilder>(context, - CertificateConfirmation.class).addSegment(fingerPrint) - .addSegment(reason.toString()).build(); - } - - private static String getFingerprint(Intent intent) { - return SegmentIntentBuilder.getSegment(intent, 0); - } - - private static CertificateInvalidReason getReason(Intent intent) { - String value = SegmentIntentBuilder.getSegment(intent, 1); - if (value != null) - try { - return CertificateInvalidReason.valueOf(value); - } catch (NoSuchElementException e) { - } - return null; - } + private static final String SAVED_SHOW_DETAILS = "com.xabber.android.ui.CertificateConfirmation.SHOW_DETAILS"; + + private PendingCertificate pendingCertificate; + private boolean showDetails; + + public static Intent createIntent(Context context, String fingerPrint, + CertificateInvalidReason reason) { + return new SegmentIntentBuilder>(context, + CertificateConfirmation.class).addSegment(fingerPrint) + .addSegment(reason.toString()).build(); + } + + private static String getFingerprint(Intent intent) { + return SegmentIntentBuilder.getSegment(intent, 0); + } + + private static CertificateInvalidReason getReason(Intent intent) { + String value = SegmentIntentBuilder.getSegment(intent, 1); + if (value != null) + try { + return CertificateInvalidReason.valueOf(value); + } catch (NoSuchElementException e) { + } + return null; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + String fingerprint = getFingerprint(getIntent()); + CertificateInvalidReason reason = getReason(getIntent()); + pendingCertificate = null; + if (fingerprint != null && reason != null) + pendingCertificate = CertificateManager.getInstance() + .getPendingCertificate(fingerprint, reason); + if (pendingCertificate == null) + finish(); + if (savedInstanceState == null) { + showDetails = false; + } else { + showDetails = savedInstanceState.getBoolean(SAVED_SHOW_DETAILS, + false); + } + ((Button) findViewById(android.R.id.button3)) + .setText(R.string.certificate_show_details); + setDialogTitle(R.string.INVALID_CERTIFICATE); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putBoolean(SAVED_SHOW_DETAILS, showDetails); + } + + @Override + protected void onResume() { + super.onResume(); + update(); + } + + private void update() { + String reason = getString(pendingCertificate.getReason() + .getResourceId()); + String message = getString(R.string.certificate_confirmation, reason, + CertificateManager.showFingerprint(pendingCertificate + .getFingerprint())); + if (showDetails) { + String details = getString(R.string.certificate_details, + pendingCertificate.getIssuerCommonName(), + pendingCertificate.getIssuerOrganization(), + pendingCertificate.getIssuerOrganizationlUnit(), + pendingCertificate.getSerialNumber(), + pendingCertificate.getSubjectCommonName(), + pendingCertificate.getSubjectOrganization(), + pendingCertificate.getSubjectOrganizationlUnit(), + pendingCertificate.issuedOn(), + pendingCertificate.expiresOn()); + message += details; + findViewById(android.R.id.button3).setVisibility(View.GONE); + } + ((TextView) findViewById(android.R.id.message)).setText(message); + } + + @Override + public void onAccept() { + super.onAccept(); + CertificateManager.getInstance().accept( + pendingCertificate.getFingerprint(), + pendingCertificate.getReason()); + ConnectionManager.getInstance().updateConnections(true); + finish(); + } + + @Override + public void onDecline() { + super.onDecline(); + CertificateManager.getInstance().discard( + pendingCertificate.getFingerprint(), + pendingCertificate.getReason()); + finish(); + } + + @Override + public void onCenter() { + super.onCenter(); + showDetails = true; + update(); + } } diff --git a/app/src/main/java/com/xabber/android/ui/ChatEditor.java b/app/src/main/java/com/xabber/android/ui/ChatEditor.java deleted file mode 100644 index b13b58228b..0000000000 --- a/app/src/main/java/com/xabber/android/ui/ChatEditor.java +++ /dev/null @@ -1,113 +0,0 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.xabber.android.ui; - -import java.util.HashMap; -import java.util.Map; - -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; - -import com.xabber.android.data.Application; -import com.xabber.android.data.account.AccountItem; -import com.xabber.android.data.account.AccountManager; -import com.xabber.android.data.account.ArchiveMode; -import com.xabber.android.data.intent.EntityIntentBuilder; -import com.xabber.android.data.message.chat.ChatManager; -import com.xabber.android.ui.helper.BaseSettingsActivity; -import com.xabber.androiddev.R; - -public class ChatEditor extends BaseSettingsActivity { - - private String account; - private String user; - - @Override - protected void onInflate(Bundle savedInstanceState) { - account = getAccount(getIntent()); - user = getUser(getIntent()); - AccountItem accountItem = AccountManager.getInstance().getAccount( - account); - if (accountItem == null || user == null) { - Application.getInstance().onError(R.string.ENTRY_IS_NOT_FOUND); - finish(); - return; - } - addPreferencesFromResource(R.xml.chat_editor); - if (accountItem.getArchiveMode() == ArchiveMode.server - || accountItem.getArchiveMode() == ArchiveMode.dontStore) - getPreferenceScreen().removePreference( - getPreferenceScreen().findPreference( - getString(R.string.chat_save_history_key))); - } - - @Override - protected Map getValues() { - Map map = new HashMap(); - putValue(map, R.string.chat_save_history_key, ChatManager.getInstance() - .isSaveMessages(account, user)); - putValue(map, R.string.chat_events_visible_chat_key, ChatManager - .getInstance().isNotifyVisible(account, user)); - putValue(map, R.string.chat_events_show_text_key, ChatManager - .getInstance().isShowText(account, user)); - putValue(map, R.string.chat_events_vibro_key, ChatManager.getInstance() - .isMakeVibro(account, user)); - putValue(map, R.string.chat_events_sound_key, ChatManager.getInstance() - .getSound(account, user)); - return map; - } - - @Override - protected boolean setValues(Map source, - Map result) { - if (hasChanges(source, result, R.string.chat_save_history_key)) - ChatManager.getInstance().setSaveMessages(account, user, - getBoolean(result, R.string.chat_save_history_key)); - - if (hasChanges(source, result, R.string.chat_events_visible_chat_key)) - ChatManager.getInstance().setNotifyVisible(account, user, - getBoolean(result, R.string.chat_events_visible_chat_key)); - - if (hasChanges(source, result, R.string.chat_events_show_text_key)) - ChatManager.getInstance().setShowText(account, user, - getBoolean(result, R.string.chat_events_show_text_key)); - - if (hasChanges(source, result, R.string.chat_events_vibro_key)) - ChatManager.getInstance().setMakeVibro(account, user, - getBoolean(result, R.string.chat_events_vibro_key)); - - if (hasChanges(source, result, R.string.chat_events_sound_key)) - ChatManager.getInstance().setSound(account, user, - getUri(result, R.string.chat_events_sound_key)); - - return true; - } - - public static Intent createIntent(Context context, String account, - String user) { - return new EntityIntentBuilder(context, ChatEditor.class) - .setAccount(account).setUser(user).build(); - } - - private static String getAccount(Intent intent) { - return EntityIntentBuilder.getAccount(intent); - } - - private static String getUser(Intent intent) { - return EntityIntentBuilder.getUser(intent); - } - -} diff --git a/app/src/main/java/com/xabber/android/ui/ChatList.java b/app/src/main/java/com/xabber/android/ui/ChatList.java deleted file mode 100644 index b3e2ce3f4d..0000000000 --- a/app/src/main/java/com/xabber/android/ui/ChatList.java +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.xabber.android.ui; - -import java.util.Collection; - -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.view.View; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemClickListener; - -import com.xabber.android.data.Application; -import com.xabber.android.data.account.OnAccountChangedListener; -import com.xabber.android.data.entity.BaseEntity; -import com.xabber.android.data.message.AbstractChat; -import com.xabber.android.data.message.OnChatChangedListener; -import com.xabber.android.data.roster.OnContactChangedListener; -import com.xabber.android.ui.adapter.ChatListAdapter; -import com.xabber.android.ui.helper.ManagedListActivity; -import com.xabber.androiddev.R; - -public class ChatList extends ManagedListActivity implements - OnAccountChangedListener, OnContactChangedListener, - OnChatChangedListener, OnItemClickListener { - - private ChatListAdapter listAdapter; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (isFinishing()) - return; - - setContentView(R.layout.list); - listAdapter = new ChatListAdapter(this); - setListAdapter(listAdapter); - getListView().setOnItemClickListener(this); - } - - @Override - protected void onResume() { - super.onResume(); - Application.getInstance().addUIListener(OnAccountChangedListener.class, - this); - Application.getInstance().addUIListener(OnContactChangedListener.class, - this); - Application.getInstance().addUIListener(OnChatChangedListener.class, - this); - listAdapter.onChange(); - } - - @Override - protected void onPause() { - super.onPause(); - Application.getInstance().removeUIListener( - OnAccountChangedListener.class, this); - Application.getInstance().removeUIListener( - OnContactChangedListener.class, this); - Application.getInstance().removeUIListener(OnChatChangedListener.class, - this); - } - - @Override - public void onChatChanged(String account, String user, boolean incoming) { - listAdapter.onChange(); - } - - @Override - public void onContactsChanged(Collection addresses) { - listAdapter.onChange(); - } - - @Override - public void onAccountsChanged(Collection accounts) { - listAdapter.onChange(); - } - - @Override - public void onItemClick(AdapterView parent, View view, int position, - long id) { - AbstractChat abstractChat = (AbstractChat) parent.getAdapter().getItem( - position); - startActivity(ChatViewer.createIntent(this, abstractChat.getAccount(), - abstractChat.getUser())); - finish(); - } - - public static Intent createIntent(Context context) { - return new Intent(context, ChatList.class); - } - -} diff --git a/app/src/main/java/com/xabber/android/ui/ChatViewer.java b/app/src/main/java/com/xabber/android/ui/ChatViewer.java index de7023bf3c..9753de1899 100644 --- a/app/src/main/java/com/xabber/android/ui/ChatViewer.java +++ b/app/src/main/java/com/xabber/android/ui/ChatViewer.java @@ -1,881 +1,519 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.ui; -import java.io.File; -import java.util.Collection; - -import android.app.Dialog; +import android.app.Activity; import android.content.Context; import android.content.Intent; -import android.net.Uri; -import android.os.AsyncTask; import android.os.Bundle; -import android.text.ClipboardManager; -import android.view.ContextMenu; -import android.view.ContextMenu.ContextMenuInfo; -import android.view.KeyEvent; -import android.view.Menu; -import android.view.MenuItem; -import android.view.SubMenu; +import android.support.v4.view.ViewPager; import android.view.View; -import android.view.inputmethod.EditorInfo; +import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; -import android.widget.AdapterView.AdapterContextMenuInfo; -import android.widget.EditText; -import android.widget.ListView; -import android.widget.TextView; -import android.widget.TextView.OnEditorActionListener; -import android.widget.Toast; +import android.widget.LinearLayout; +import com.xabber.android.R; import com.xabber.android.data.ActivityManager; import com.xabber.android.data.Application; -import com.xabber.android.data.LogManager; -import com.xabber.android.data.NetworkException; import com.xabber.android.data.SettingsManager; -import com.xabber.android.data.SettingsManager.ChatsHideKeyboard; -import com.xabber.android.data.SettingsManager.SecurityOtrMode; import com.xabber.android.data.account.OnAccountChangedListener; import com.xabber.android.data.entity.BaseEntity; import com.xabber.android.data.extension.archive.MessageArchiveManager; import com.xabber.android.data.extension.attention.AttentionManager; -import com.xabber.android.data.extension.cs.ChatStateManager; -import com.xabber.android.data.extension.muc.MUCManager; -import com.xabber.android.data.extension.muc.RoomChat; -import com.xabber.android.data.extension.muc.RoomState; -import com.xabber.android.data.extension.otr.OTRManager; -import com.xabber.android.data.extension.otr.SecurityLevel; import com.xabber.android.data.intent.EntityIntentBuilder; import com.xabber.android.data.message.AbstractChat; -import com.xabber.android.data.message.MessageItem; import com.xabber.android.data.message.MessageManager; import com.xabber.android.data.message.OnChatChangedListener; -import com.xabber.android.data.message.RegularChat; import com.xabber.android.data.notification.NotificationManager; import com.xabber.android.data.roster.OnContactChangedListener; +import com.xabber.android.ui.adapter.ChatScrollIndicatorAdapter; import com.xabber.android.ui.adapter.ChatViewerAdapter; -import com.xabber.android.ui.adapter.OnTextChangedListener; -import com.xabber.android.ui.dialog.ConfirmDialogListener; -import com.xabber.android.ui.dialog.DialogBuilder; -import com.xabber.android.ui.dialog.ExportChatDialogBuilder; import com.xabber.android.ui.helper.ManagedActivity; -import com.xabber.android.ui.widget.PageSwitcher; -import com.xabber.android.ui.widget.PageSwitcher.OnSelectListener; -import com.xabber.androiddev.R; +import com.xabber.android.ui.helper.StatusBarPainter; + +import java.util.Collection; +import java.util.HashSet; /** * Chat activity. - * - * Warning: {@link PageSwitcher} is to be removed and related implementation is - * to be fixed. - * + *

+ * * @author alexander.ivanov - * */ -public class ChatViewer extends ManagedActivity implements - View.OnClickListener, View.OnKeyListener, OnSelectListener, - OnChatChangedListener, OnContactChangedListener, - OnAccountChangedListener, OnEditorActionListener, - ConfirmDialogListener, OnTextChangedListener { - - /** - * Attention request. - */ - private static final String ACTION_ATTENTION = "com.xabber.android.data.ATTENTION"; - - /** - * Minimum number of new messages to be requested from the server side - * archive. - */ - private static final int MINIMUM_MESSAGES_TO_LOAD = 10; - - private static final String SAVED_ACCOUNT = "com.xabber.android.ui.ChatViewer.SAVED_ACCOUNT"; - private static final String SAVED_USER = "com.xabber.android.ui.ChatViewer.SAVED_USER"; - private static final String SAVED_EXIT_ON_SEND = "com.xabber.android.ui.ChatViewer.EXIT_ON_SEND"; - - private static final int OPTION_MENU_VIEW_CONTACT_ID = 0x02; - private static final int OPTION_MENU_CHAT_LIST_ID = 0x03; - private static final int OPTION_MENU_CLOSE_CHAT_ID = 0x04; - private static final int OPTION_MENU_SHOW_HISTORY_ID = 0x05; - private static final int OPTION_MENU_SETTINGS_ID = 0x08; - private static final int OPTION_MENU_CLEAR_HISTORY_ID = 0x09; - private static final int OPTION_MENU_CLEAR_MESSAGE_ID = 0x0a; - private static final int OPTION_MENU_EXPORT_CHAT_ID = 0x0c; - private static final int OPTION_MENU_CALL_ATTENTION_ID = 0x0d; - - private static final int OPTION_MENU_LEAVE_ROOM_ID = 0x10; - private static final int OPTION_MENU_JOIN_ROOM_ID = 0x11; - private static final int OPTION_MENU_MUC_INVITE_ID = 0x12; - private static final int OPTION_MENU_EDIT_ROOM_ID = 0x13; - private static final int OPTION_MENU_OCCUPANT_LIST_ID = 0x14; - - private static final int OPTION_MENU_START_OTR_ID = 0x20; - private static final int OPTION_MENU_END_OTR_ID = 0x21; - private static final int OPTION_MENU_VERIFY_FINGERPRINT_ID = 0x22; - private static final int OPTION_MENU_VERIFY_QUESTION_ID = 0x23; - private static final int OPTION_MENU_VERIFY_SECRET_ID = 0x24; - private static final int OPTION_MENU_REFRESH_OTR_ID = 0x25; - - private static final int CONTEXT_MENU_QUOTE_ID = 0x100; - private static final int CONTEXT_MENU_REPEAT_ID = 0x101; - private static final int CONTEXT_MENU_COPY_ID = 0x102; - private static final int CONTEXT_MENU_REMOVE_ID = 0x103; - - private static final int DIALOG_EXPORT_CHAT_ID = 0x200; - - private ChatViewerAdapter chatViewerAdapter; - private PageSwitcher pageSwitcher; - - private String actionWithAccount; - private String actionWithUser; - private View actionWithView; - private MessageItem actionWithMessage; - - private boolean exitOnSend; - - private boolean isVisible; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (isFinishing()) - return; - - Intent intent = getIntent(); - String account = getAccount(intent); - String user = getUser(intent); - if (PageSwitcher.LOG) - LogManager.i(this, "Intent: " + account + ":" + user); - if (account == null || user == null) { - Application.getInstance().onError(R.string.ENTRY_IS_NOT_FOUND); - finish(); - return; - } - if (hasAttention(intent)) - AttentionManager.getInstance().removeAccountNotifications(account, - user); - actionWithAccount = null; - actionWithUser = null; - actionWithView = null; - actionWithMessage = null; - - setContentView(R.layout.chat_viewer); - chatViewerAdapter = new ChatViewerAdapter(this, account, user); - chatViewerAdapter.setOnClickListener(this); - chatViewerAdapter.setOnKeyListener(this); - chatViewerAdapter.setOnEditorActionListener(this); - chatViewerAdapter.setOnCreateContextMenuListener(this); - chatViewerAdapter.setOnTextChangedListener(this); - pageSwitcher = (PageSwitcher) findViewById(R.id.switcher); - pageSwitcher.setAdapter(chatViewerAdapter); - pageSwitcher.setOnSelectListener(this); - - if (savedInstanceState != null) { - actionWithAccount = savedInstanceState.getString(SAVED_ACCOUNT); - actionWithUser = savedInstanceState.getString(SAVED_USER); - exitOnSend = savedInstanceState.getBoolean(SAVED_EXIT_ON_SEND); - } - if (actionWithAccount == null) - actionWithAccount = account; - if (actionWithUser == null) - actionWithUser = user; - - selectChat(actionWithAccount, actionWithUser); - } - - @Override - protected void onResume() { - super.onResume(); - Application.getInstance().addUIListener(OnChatChangedListener.class, - this); - Application.getInstance().addUIListener(OnContactChangedListener.class, - this); - Application.getInstance().addUIListener(OnAccountChangedListener.class, - this); - chatViewerAdapter.onChange(); - if (actionWithView != null) - chatViewerAdapter.onChatChange(actionWithView, false); - Intent intent = getIntent(); - if (Intent.ACTION_SEND.equals(intent.getAction())) { - String additional = intent.getStringExtra(Intent.EXTRA_TEXT); - if (additional != null) { - intent.removeExtra(Intent.EXTRA_TEXT); - exitOnSend = true; - if (actionWithView != null) - insertText(additional); - } - } - isVisible = true; - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - if (PageSwitcher.LOG) - LogManager.i(this, "onSave: " + actionWithAccount + ":" - + actionWithUser); - outState.putString(SAVED_ACCOUNT, actionWithAccount); - outState.putString(SAVED_USER, actionWithUser); - outState.putBoolean(SAVED_EXIT_ON_SEND, exitOnSend); - } - - @Override - protected void onPause() { - super.onPause(); - Application.getInstance().removeUIListener(OnChatChangedListener.class, - this); - Application.getInstance().removeUIListener( - OnContactChangedListener.class, this); - Application.getInstance().removeUIListener( - OnAccountChangedListener.class, this); - MessageManager.getInstance().removeVisibleChat(); - pageSwitcher.saveState(); - isVisible = false; - } - - @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - if (isFinishing()) - return; - - String account = getAccount(intent); - String user = getUser(intent); - if (account == null || user == null) { - Application.getInstance().onError(R.string.ENTRY_IS_NOT_FOUND); - return; - } - if (hasAttention(intent)) - AttentionManager.getInstance().removeAccountNotifications(account, - user); - - chatViewerAdapter.onChange(); - if (!selectChat(account, user)) - Application.getInstance().onError(R.string.ENTRY_IS_NOT_FOUND); - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - super.onPrepareOptionsMenu(menu); - menu.clear(); - AbstractChat abstractChat = MessageManager.getInstance().getChat( - actionWithAccount, actionWithUser); - if (abstractChat != null && abstractChat instanceof RoomChat) { - if (((RoomChat) abstractChat).getState() == RoomState.unavailable) - menu.add(0, OPTION_MENU_JOIN_ROOM_ID, 0, - getResources().getText(R.string.muc_join)).setIcon( - android.R.drawable.ic_menu_add); - else - menu.add(0, OPTION_MENU_MUC_INVITE_ID, 0, - getResources().getText(R.string.muc_invite)).setIcon( - android.R.drawable.ic_menu_add); - } else { - menu.add(0, OPTION_MENU_VIEW_CONTACT_ID, 0, - getResources().getText(R.string.contact_editor)).setIcon( - android.R.drawable.ic_menu_edit); - } - menu.add(0, OPTION_MENU_CHAT_LIST_ID, 0, getText(R.string.chat_list)) - .setIcon(R.drawable.ic_menu_friendslist); - menu.add(0, OPTION_MENU_SETTINGS_ID, 0, getText(R.string.chat_settings)) - .setIcon(android.R.drawable.ic_menu_preferences); - menu.add(0, OPTION_MENU_SHOW_HISTORY_ID, 0, - getText(R.string.show_history)).setIcon( - R.drawable.ic_menu_archive); - if (abstractChat != null - && abstractChat instanceof RoomChat - && ((RoomChat) abstractChat).getState() != RoomState.unavailable) { - if (((RoomChat) abstractChat).getState() == RoomState.error) - menu.add(0, OPTION_MENU_EDIT_ROOM_ID, 0, - getResources().getText(R.string.muc_edit)).setIcon( - android.R.drawable.ic_menu_edit); - else - menu.add(0, OPTION_MENU_LEAVE_ROOM_ID, 0, - getResources().getText(R.string.muc_leave)).setIcon( - android.R.drawable.ic_menu_close_clear_cancel); - } else { - menu.add(0, OPTION_MENU_CLOSE_CHAT_ID, 0, - getResources().getText(R.string.close_chat)).setIcon( - android.R.drawable.ic_menu_close_clear_cancel); - } - menu.add(0, OPTION_MENU_CLEAR_MESSAGE_ID, 0, - getResources().getText(R.string.clear_message)).setIcon( - R.drawable.ic_menu_stop); - menu.add(0, OPTION_MENU_CLEAR_HISTORY_ID, 0, - getText(R.string.clear_history)); - menu.add(0, OPTION_MENU_EXPORT_CHAT_ID, 0, - getText(R.string.export_chat)); - if (abstractChat != null && abstractChat instanceof RegularChat) { - menu.add(0, OPTION_MENU_CALL_ATTENTION_ID, 0, - getText(R.string.call_attention)); - SecurityLevel securityLevel = OTRManager.getInstance() - .getSecurityLevel(abstractChat.getAccount(), - abstractChat.getUser()); - SubMenu otrMenu = menu.addSubMenu(getText(R.string.otr_encryption)); - otrMenu.setHeaderTitle(R.string.otr_encryption); - if (securityLevel == SecurityLevel.plain) - otrMenu.add(0, OPTION_MENU_START_OTR_ID, 0, - getText(R.string.otr_start)) - .setEnabled( - SettingsManager.securityOtrMode() != SecurityOtrMode.disabled); - else - otrMenu.add(0, OPTION_MENU_REFRESH_OTR_ID, 0, - getText(R.string.otr_refresh)); - otrMenu.add(0, OPTION_MENU_END_OTR_ID, 0, getText(R.string.otr_end)) - .setEnabled(securityLevel != SecurityLevel.plain); - otrMenu.add(0, OPTION_MENU_VERIFY_FINGERPRINT_ID, 0, - getText(R.string.otr_verify_fingerprint)).setEnabled( - securityLevel != SecurityLevel.plain); - otrMenu.add(0, OPTION_MENU_VERIFY_QUESTION_ID, 0, - getText(R.string.otr_verify_question)).setEnabled( - securityLevel != SecurityLevel.plain); - otrMenu.add(0, OPTION_MENU_VERIFY_SECRET_ID, 0, - getText(R.string.otr_verify_secret)).setEnabled( - securityLevel != SecurityLevel.plain); - } - if (abstractChat != null && abstractChat instanceof RoomChat - && ((RoomChat) abstractChat).getState() == RoomState.available) - menu.add(0, OPTION_MENU_OCCUPANT_LIST_ID, 0, getResources() - .getText(R.string.occupant_list)); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - super.onOptionsItemSelected(item); - switch (item.getItemId()) { - case OPTION_MENU_VIEW_CONTACT_ID: - startActivity(ContactEditor.createIntent(this, actionWithAccount, - actionWithUser)); - return true; - case OPTION_MENU_CHAT_LIST_ID: - startActivity(ChatList.createIntent(this)); - return true; - case OPTION_MENU_CLOSE_CHAT_ID: - MessageManager.getInstance().closeChat(actionWithAccount, - actionWithUser); - NotificationManager.getInstance().removeMessageNotification( - actionWithAccount, actionWithUser); - close(); - return true; - case OPTION_MENU_CLEAR_HISTORY_ID: - MessageManager.getInstance().clearHistory(actionWithAccount, - actionWithUser); - chatViewerAdapter.onChatChange(actionWithView, false); - return true; - case OPTION_MENU_SHOW_HISTORY_ID: - MessageManager.getInstance().requestToLoadLocalHistory( - actionWithAccount, actionWithUser); - MessageArchiveManager.getInstance().requestHistory( - actionWithAccount, actionWithUser, - MINIMUM_MESSAGES_TO_LOAD, 0); - chatViewerAdapter.onChange(); - chatViewerAdapter.onChatChange(actionWithView, false); - return true; - case OPTION_MENU_SETTINGS_ID: - startActivity(ChatEditor.createIntent(this, actionWithAccount, - actionWithUser)); - return true; - case OPTION_MENU_CLEAR_MESSAGE_ID: - ((EditText) actionWithView.findViewById(R.id.chat_input)) - .setText(""); - return true; - case OPTION_MENU_EXPORT_CHAT_ID: - showDialog(DIALOG_EXPORT_CHAT_ID); - return true; - case OPTION_MENU_CALL_ATTENTION_ID: - try { - AttentionManager.getInstance().sendAttention(actionWithAccount, - actionWithUser); - } catch (NetworkException e) { - Application.getInstance().onError(e); - } - return true; - case OPTION_MENU_JOIN_ROOM_ID: - MUCManager.getInstance().joinRoom(actionWithAccount, - actionWithUser, true); - return true; - case OPTION_MENU_LEAVE_ROOM_ID: - MUCManager.getInstance().leaveRoom(actionWithAccount, - actionWithUser); - MessageManager.getInstance().closeChat(actionWithAccount, - actionWithUser); - NotificationManager.getInstance().removeMessageNotification( - actionWithAccount, actionWithUser); - close(); - return true; - case OPTION_MENU_MUC_INVITE_ID: - startActivity(ContactList.createRoomInviteIntent(this, - actionWithAccount, actionWithUser)); - return true; - case OPTION_MENU_EDIT_ROOM_ID: - startActivity(MUCEditor.createIntent(this, actionWithAccount, - actionWithUser)); - return true; - case OPTION_MENU_OCCUPANT_LIST_ID: - startActivity(OccupantList.createIntent(this, actionWithAccount, - actionWithUser)); - return true; - case OPTION_MENU_START_OTR_ID: - try { - OTRManager.getInstance().startSession(actionWithAccount, - actionWithUser); - } catch (NetworkException e) { - Application.getInstance().onError(e); - } - return true; - case OPTION_MENU_REFRESH_OTR_ID: - try { - OTRManager.getInstance().refreshSession(actionWithAccount, - actionWithUser); - } catch (NetworkException e) { - Application.getInstance().onError(e); - } - return true; - case OPTION_MENU_END_OTR_ID: - try { - OTRManager.getInstance().endSession(actionWithAccount, - actionWithUser); - } catch (NetworkException e) { - Application.getInstance().onError(e); - } - return true; - case OPTION_MENU_VERIFY_FINGERPRINT_ID: - startActivity(FingerprintViewer.createIntent(this, - actionWithAccount, actionWithUser)); - return true; - case OPTION_MENU_VERIFY_QUESTION_ID: - startActivity(QuestionViewer.createIntent(this, actionWithAccount, - actionWithUser, true, false, null)); - return true; - case OPTION_MENU_VERIFY_SECRET_ID: - startActivity(QuestionViewer.createIntent(this, actionWithAccount, - actionWithUser, false, false, null)); - return true; - } - return false; - } - - @Override - public void onCreateContextMenu(ContextMenu menu, View view, - ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, view, menuInfo); - AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; - pageSwitcher.stopMovement(); - - ListView listView = (ListView) actionWithView - .findViewById(android.R.id.list); - actionWithMessage = (MessageItem) listView.getAdapter().getItem( - info.position); - if (actionWithMessage != null && actionWithMessage.getAction() != null) - actionWithMessage = null; // Skip action message - if (actionWithMessage == null) - return; - if (actionWithMessage.isError()) { - menu.add(0, CONTEXT_MENU_REPEAT_ID, 0, - getResources().getText(R.string.message_repeat)); - } - menu.add(0, CONTEXT_MENU_QUOTE_ID, 0, - getResources().getText(R.string.message_quote)); - menu.add(0, CONTEXT_MENU_COPY_ID, 0, - getResources().getText(R.string.message_copy)); - menu.add(0, CONTEXT_MENU_REMOVE_ID, 0, - getResources().getText(R.string.message_remove)); - } - - /** - * Insert additional text to the input. - * - * @param additional - */ - private void insertText(String additional) { - EditText editView = (EditText) actionWithView - .findViewById(R.id.chat_input); - String source = editView.getText().toString(); - int selection = editView.getSelectionEnd(); - if (selection == -1) - selection = source.length(); - else if (selection > source.length()) - selection = source.length(); - String before = source.substring(0, selection); - String after = source.substring(selection); - if (before.length() > 0 && !before.endsWith("\n")) - additional = "\n" + additional; - editView.setText(before + additional + after); - editView.setSelection(selection + additional.length()); - } - - @Override - public boolean onContextItemSelected(MenuItem item) { - if (actionWithMessage == null) - return false; - super.onContextItemSelected(item); - switch (item.getItemId()) { - case CONTEXT_MENU_QUOTE_ID: - insertText("> " + actionWithMessage.getText() + "\n"); - return true; - case CONTEXT_MENU_REPEAT_ID: - sendMessage(actionWithMessage.getText()); - return true; - case CONTEXT_MENU_COPY_ID: - ((ClipboardManager) getSystemService(CLIPBOARD_SERVICE)) - .setText(actionWithMessage.getSpannable()); - return true; - case CONTEXT_MENU_REMOVE_ID: - MessageManager.getInstance().removeMessage(actionWithMessage); - chatViewerAdapter.onChatChange(actionWithView, false); - return true; - } - return false; - } - - @Override - protected Dialog onCreateDialog(int id) { - super.onCreateDialog(id); - switch (id) { - case DIALOG_EXPORT_CHAT_ID: - return new ExportChatDialogBuilder(this, DIALOG_EXPORT_CHAT_ID, - this, actionWithAccount, actionWithUser).create(); - default: - return null; - } - } - - @Override - public void onClick(View view) { - switch (view.getId()) { - case R.id.chat_send: - sendMessage(); - break; - case R.id.title: - ListView listView = (ListView) actionWithView - .findViewById(android.R.id.list); - int size = listView.getCount(); - if (size > 0) - listView.setSelection(size - 1); - default: - break; - } - } - - @Override - public boolean onKey(View view, int keyCode, KeyEvent event) { - if (event.getAction() == KeyEvent.ACTION_DOWN - && keyCode == KeyEvent.KEYCODE_ENTER - && SettingsManager.chatsSendByEnter()) { - sendMessage(); - return true; - } - return false; - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - switch (keyCode) { - case KeyEvent.KEYCODE_BACK: - close(); - return false; - } - return super.onKeyDown(keyCode, event); - } - - private void close() { - finish(); - if (!Intent.ACTION_SEND.equals(getIntent().getAction())) { - ActivityManager.getInstance().clearStack(false); - if (!ActivityManager.getInstance().hasContactList(this)) - startActivity(ContactList.createIntent(this)); - } - } - - @Override - public void onTextChanged(EditText editText, CharSequence text) { - ChatStateManager.getInstance().onComposing(actionWithAccount, - actionWithUser, text); - } - - @Override - public void onSelect() { - BaseEntity contactItem = (BaseEntity) pageSwitcher.getSelectedItem(); - actionWithAccount = contactItem.getAccount(); - actionWithUser = contactItem.getUser(); - if (PageSwitcher.LOG) - LogManager.i(this, "onSelect: " + actionWithAccount + ":" - + actionWithUser); - actionWithView = pageSwitcher.getSelectedView(); - actionWithMessage = null; - if (isVisible) - MessageManager.getInstance().setVisibleChat(actionWithAccount, - actionWithUser); - MessageArchiveManager.getInstance().requestHistory( - actionWithAccount, - actionWithUser, - 0, - MessageManager.getInstance() - .getChat(actionWithAccount, actionWithUser) - .getRequiredMessageCount()); - NotificationManager.getInstance().removeMessageNotification( - actionWithAccount, actionWithUser); - } - - @Override - public void onUnselect() { - actionWithAccount = null; - actionWithUser = null; - actionWithView = null; - actionWithMessage = null; - if (PageSwitcher.LOG) - LogManager.i(this, "onUnselect"); - } - - private void sendMessage() { - if (actionWithView == null) - return; - EditText editView = (EditText) actionWithView - .findViewById(R.id.chat_input); - String text = editView.getText().toString(); - int start = 0; - int end = text.length(); - while (start < end - && (text.charAt(start) == ' ' || text.charAt(start) == '\n')) - start += 1; - while (start < end - && (text.charAt(end - 1) == ' ' || text.charAt(end - 1) == '\n')) - end -= 1; - text = text.substring(start, end); - if ("".equals(text)) - return; - chatViewerAdapter.setOnTextChangedListener(null); - editView.setText(""); - chatViewerAdapter.setOnTextChangedListener(this); - sendMessage(text); - if (exitOnSend) - close(); - if (SettingsManager.chatsHideKeyboard() == ChatsHideKeyboard.always - || (getResources().getBoolean(R.bool.landscape) && SettingsManager - .chatsHideKeyboard() == ChatsHideKeyboard.landscape)) { - InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(editView.getWindowToken(), 0); - } - } - - private void sendMessage(String text) { - MessageManager.getInstance().sendMessage(actionWithAccount, - actionWithUser, text); - chatViewerAdapter.onChatChange(actionWithView, false); - } - - @Override - public void onChatChanged(final String account, final String user, - final boolean incoming) { - BaseEntity baseEntity; - baseEntity = (BaseEntity) pageSwitcher.getSelectedItem(); - if (baseEntity != null && baseEntity.equals(account, user)) { - chatViewerAdapter.onChatChange(pageSwitcher.getSelectedView(), - incoming); - return; - } - baseEntity = (BaseEntity) pageSwitcher.getVisibleItem(); - if (baseEntity != null && baseEntity.equals(account, user)) { - chatViewerAdapter.onChatChange(pageSwitcher.getVisibleView(), - incoming); - return; - } - // Search for chat in adapter. - final int count = chatViewerAdapter.getCount(); - for (int index = 0; index < count; index++) - if (((BaseEntity) chatViewerAdapter.getItem(index)).equals(account, - user)) - return; - // New chat. - chatViewerAdapter.onChange(); - } - - @Override - public void onContactsChanged(Collection entities) { - BaseEntity baseEntity; - baseEntity = (BaseEntity) pageSwitcher.getSelectedItem(); - if (baseEntity != null && entities.contains(baseEntity)) { - chatViewerAdapter.onChange(); - return; - } - baseEntity = (BaseEntity) pageSwitcher.getVisibleItem(); - if (baseEntity != null && entities.contains(baseEntity)) { - chatViewerAdapter.onChange(); - return; - } - } - - @Override - public void onAccountsChanged(Collection accounts) { - BaseEntity baseEntity; - baseEntity = (BaseEntity) pageSwitcher.getSelectedItem(); - if (baseEntity != null && accounts.contains(baseEntity.getAccount())) { - chatViewerAdapter.onChange(); - return; - } - baseEntity = (BaseEntity) pageSwitcher.getVisibleItem(); - if (baseEntity != null && accounts.contains(baseEntity.getAccount())) { - chatViewerAdapter.onChange(); - return; - } - } - - @Override - public boolean onEditorAction(TextView view, int actionId, KeyEvent event) { - if (actionId == EditorInfo.IME_ACTION_SEND) { - sendMessage(); - return true; - } - return false; - } - - @Override - public void onAccept(DialogBuilder dialogBuilder) { - switch (dialogBuilder.getDialogId()) { - case DIALOG_EXPORT_CHAT_ID: - ExportChatDialogBuilder builder = (ExportChatDialogBuilder) dialogBuilder; - exportChat(builder); - } - } - - private void exportChat(ExportChatDialogBuilder dialogBuilder) { - //TODO: retain AsyncTask via retained fragment - new ChatExportAsyncTask(dialogBuilder).execute(); - } - - private class ChatExportAsyncTask extends AsyncTask { - private ExportChatDialogBuilder builder; - - public ChatExportAsyncTask(ExportChatDialogBuilder builder) { - this.builder = builder; - } - - @Override - protected File doInBackground(Void... params) { - File file = null; - try { - file = MessageManager.getInstance().exportChat( - actionWithAccount, actionWithUser, builder.getName()); - } catch (NetworkException e) { - Application.getInstance().onError(e); - } - return file; - } - - @Override - public void onPostExecute(File result) { - if (result != null) { - if (builder.isSendChecked()) { - Intent intent = new Intent(android.content.Intent.ACTION_SEND); - intent.setType("text/plain"); - Uri uri = Uri.fromFile(result); - intent.putExtra(android.content.Intent.EXTRA_STREAM, uri); - startActivity(Intent.createChooser(intent, - getString(R.string.export_chat))); - } else { - Toast.makeText(ChatViewer.this, R.string.export_chat_done, - Toast.LENGTH_LONG).show(); - } - } - } - - } - - @Override - public void onDecline(DialogBuilder dialogBuilder) { - } - - @Override - public void onCancel(DialogBuilder dialogBuilder) { - } - - private boolean selectChat(String account, String user) { - for (int position = 0; position < chatViewerAdapter.getCount(); position++) - if (((BaseEntity) chatViewerAdapter.getItem(position)).equals( - account, user)) { - if (PageSwitcher.LOG) - LogManager.i(this, "setSelection: " + position + ", " - + account + ":" + user); - pageSwitcher.setSelection(position); - return true; - } - if (PageSwitcher.LOG) - LogManager.i(this, "setSelection: not found, " + account + ":" - + user); - return false; - } - - public static Intent createIntent(Context context, String account, - String user) { - return new EntityIntentBuilder(context, ChatViewer.class) - .setAccount(account).setUser(user).build(); - } - - public static Intent createClearTopIntent(Context context, String account, - String user) { - Intent intent = createIntent(context, account, user); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - return intent; - } - - /** - * Create intent to send message. - * - * Contact list will not be shown on when chat will be closed. - * - * @param context - * @param account - * @param user - * @param text - * if null then user will be able to send a number - * of messages. Else only one message can be send. - * @return - */ - public static Intent createSendIntent(Context context, String account, - String user, String text) { - Intent intent = ChatViewer.createIntent(context, account, user); - intent.setAction(Intent.ACTION_SEND); - intent.putExtra(Intent.EXTRA_TEXT, text); - return intent; - } - - public static Intent createAttentionRequestIntent(Context context, - String account, String user) { - Intent intent = ChatViewer.createClearTopIntent(context, account, user); - intent.setAction(ACTION_ATTENTION); - return intent; - } - - private static String getAccount(Intent intent) { - String value = EntityIntentBuilder.getAccount(intent); - if (value != null) - return value; - // Backward compatibility. - return intent.getStringExtra("com.xabber.android.data.account"); - } - - private static String getUser(Intent intent) { - String value = EntityIntentBuilder.getUser(intent); - if (value != null) - return value; - // Backward compatibility. - return intent.getStringExtra("com.xabber.android.data.user"); - } - - private static boolean hasAttention(Intent intent) { - return ACTION_ATTENTION.equals(intent.getAction()); - } - +public class ChatViewer extends ManagedActivity implements OnChatChangedListener, + OnContactChangedListener, OnAccountChangedListener, ViewPager.OnPageChangeListener, + ChatViewerAdapter.FinishUpdateListener, RecentChatFragment.RecentChatFragmentInteractionListener, + ChatViewerFragment.ChatViewerFragmentListener { + + /** + * Attention request. + */ + private static final String ACTION_ATTENTION = "com.xabber.android.data.ATTENTION"; + private static final String ACTION_RECENT_CHATS = "com.xabber.android.data.RECENT_CHATS"; + private static final String ACTION_SPECIFIC_CHAT = "com.xabber.android.data.ACTION_SPECIFIC_CHAT"; + private static final String ACTION_SHORTCUT = "com.xabber.android.data.ACTION_SHORTCUT"; + + private static final String SAVED_INITIAL_ACCOUNT = "com.xabber.android.ui.ChatViewer.SAVED_INITIAL_ACCOUNT"; + private static final String SAVED_INITIAL_USER = "com.xabber.android.ui.ChatViewer.SAVED_INITIAL_USER"; + + private static final String SAVED_IS_RECENT_CHATS_SELECTED = "com.xabber.android.ui.ChatViewer.SAVED_IS_RECENT_CHATS_SELECTED"; + private static final String SAVED_SELECTED_ACCOUNT = "com.xabber.android.ui.ChatViewer.SAVED_SELECTED_ACCOUNT"; + private static final String SAVED_SELECTED_USER = "com.xabber.android.ui.ChatViewer.SAVED_SELECTED_USER"; + + private static final String SAVED_EXIT_ON_SEND = "com.xabber.android.ui.ChatViewer.EXIT_ON_SEND"; + ChatScrollIndicatorAdapter chatScrollIndicatorAdapter; + ChatViewerAdapter chatViewerAdapter; + ViewPager viewPager; + Collection registeredChats = new HashSet<>(); + Collection recentChatFragments = new HashSet<>(); + private boolean exitOnSend; + private String extraText = null; + private BaseEntity initialChat = null; + private BaseEntity selectedChat = null; + + private StatusBarPainter statusBarPainter; + + private boolean isRecentChatsSelected; + + private boolean isVisible; + + public static void hideKeyboard(Activity activity) { + // Check if no view has focus: + View view = activity.getCurrentFocus(); + if (view != null) { + InputMethodManager inputManager = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); + inputManager.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); + } + } + + private static String getAccount(Intent intent) { + String value = EntityIntentBuilder.getAccount(intent); + if (value != null) + return value; + // Backward compatibility. + return intent.getStringExtra("com.xabber.android.data.account"); + } + + private static String getUser(Intent intent) { + String value = EntityIntentBuilder.getUser(intent); + if (value != null) + return value; + // Backward compatibility. + return intent.getStringExtra("com.xabber.android.data.user"); + } + + private static boolean hasAttention(Intent intent) { + return ACTION_ATTENTION.equals(intent.getAction()); + } + + public static Intent createSpecificChatIntent(Context context, String account, String user) { + Intent intent = new EntityIntentBuilder(context, ChatViewer.class).setAccount(account).setUser(user).build(); + intent.setAction(ACTION_SPECIFIC_CHAT); + return intent; + } + + public static Intent createRecentChatsIntent(Context context) { + Intent intent = new EntityIntentBuilder(context, ChatViewer.class).build(); + intent.setAction(ACTION_RECENT_CHATS); + return intent; + } + + public static Intent createClearTopIntent(Context context, String account, String user) { + Intent intent = createSpecificChatIntent(context, account, user); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + return intent; + } + + public static Intent createShortCutIntent(Context context, String account, String user) { + Intent intent = createClearTopIntent(context, account, user); + intent.setAction(ACTION_SHORTCUT); + return intent; + } + + /** + * Create intent to send message. + *

+ * Contact list will not be shown on when chat will be closed. + * + * @param context + * @param account + * @param user + * @param text if null then user will be able to send a number + * of messages. Else only one message can be send. + * @return + */ + public static Intent createSendIntent(Context context, String account, String user, String text) { + Intent intent = ChatViewer.createSpecificChatIntent(context, account, user); + intent.setAction(Intent.ACTION_SEND); + intent.putExtra(Intent.EXTRA_TEXT, text); + return intent; + } + + public static Intent createAttentionRequestIntent(Context context, String account, String user) { + Intent intent = ChatViewer.createClearTopIntent(context, account, user); + intent.setAction(ACTION_ATTENTION); + return intent; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (isFinishing()) { + return; + } + + setContentView(R.layout.chat_viewer); + statusBarPainter = new StatusBarPainter(this); + getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); + + getInitialChatFromIntent(); + getSelectedPageDataFromIntent(); + + if (savedInstanceState != null) { + restoreInstanceState(savedInstanceState); + } + + initChats(); + } + + private void initChats() { + if (initialChat != null) { + chatViewerAdapter = new ChatViewerAdapter(getFragmentManager(), initialChat, this); + } else { + chatViewerAdapter = new ChatViewerAdapter(getFragmentManager(), this); + } + + viewPager = (ViewPager) findViewById(R.id.pager); + viewPager.setAdapter(chatViewerAdapter); + viewPager.setOnPageChangeListener(this); + + if (SettingsManager.chatsShowBackground()) { + viewPager.setBackgroundDrawable(getResources().getDrawable(R.drawable.chat_background_repeat)); + } + + chatScrollIndicatorAdapter = new ChatScrollIndicatorAdapter(this, + (LinearLayout)findViewById(R.id.chat_scroll_indicator)); + chatScrollIndicatorAdapter.update(chatViewerAdapter.getActiveChats()); + } + + private void getInitialChatFromIntent() { + Intent intent = getIntent(); + String account = getAccount(intent); + String user = getUser(intent); + if (account != null && user != null) { + initialChat = new BaseEntity(account, user); + } + } + + private void getSelectedPageDataFromIntent() { + Intent intent = getIntent(); + + if (intent.getAction() == null) { + return; + } + + switch (intent.getAction()) { + case ACTION_RECENT_CHATS: + isRecentChatsSelected = true; + selectedChat = null; + break; + + case ACTION_SPECIFIC_CHAT: + case ACTION_ATTENTION: + case Intent.ACTION_SEND: + case ACTION_SHORTCUT: + isRecentChatsSelected = false; + selectedChat = new BaseEntity(getAccount(intent), getUser(intent)); + break; + } + } + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + if (isFinishing()) { + return; + } + + setIntent(intent); + + getSelectedPageDataFromIntent(); + + if (intent.getAction() != null && intent.getAction().equals(ACTION_SHORTCUT)) { + getInitialChatFromIntent(); + initChats(); + } + } + + private void restoreInstanceState(Bundle savedInstanceState) { + isRecentChatsSelected = savedInstanceState.getBoolean(SAVED_IS_RECENT_CHATS_SELECTED); + if (isRecentChatsSelected) { + selectedChat = null; + } else { + selectedChat = new BaseEntity(savedInstanceState.getString(SAVED_SELECTED_ACCOUNT), + savedInstanceState.getString(SAVED_SELECTED_USER)); + } + exitOnSend = savedInstanceState.getBoolean(SAVED_EXIT_ON_SEND); + + String initialAccount = savedInstanceState.getString(SAVED_INITIAL_ACCOUNT); + String initialUser = savedInstanceState.getString(SAVED_INITIAL_USER); + + if (initialAccount != null && initialUser != null) { + initialChat = new BaseEntity(initialAccount, initialUser); + } + } + + @Override + protected void onResume() { + super.onResume(); + isVisible = true; + + Application.getInstance().addUIListener(OnChatChangedListener.class, this); + Application.getInstance().addUIListener(OnContactChangedListener.class, this); + Application.getInstance().addUIListener(OnAccountChangedListener.class, this); + + chatViewerAdapter.updateChats(); + chatScrollIndicatorAdapter.update(chatViewerAdapter.getActiveChats()); + selectPage(); + + Intent intent = getIntent(); + + if (hasAttention(intent)) { + AttentionManager.getInstance().removeAccountNotifications(selectedChat); + } + + if (Intent.ACTION_SEND.equals(intent.getAction())) { + extraText = intent.getStringExtra(Intent.EXTRA_TEXT); + + if (extraText != null) { + intent.removeExtra(Intent.EXTRA_TEXT); + exitOnSend = true; + } + } + } + + private void selectPage() { + if (isRecentChatsSelected) { + selectRecentChatsPage(); + } else { + selectChatPage(selectedChat, false); + } + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putBoolean(SAVED_IS_RECENT_CHATS_SELECTED, isRecentChatsSelected); + if (!isRecentChatsSelected) { + outState.putString(SAVED_SELECTED_ACCOUNT, selectedChat.getAccount()); + outState.putString(SAVED_SELECTED_USER, selectedChat.getUser()); + } + outState.putBoolean(SAVED_EXIT_ON_SEND, exitOnSend); + + outState.putString(SAVED_INITIAL_ACCOUNT, initialChat.getAccount()); + outState.putString(SAVED_INITIAL_USER, initialChat.getUser()); + } + + @Override + protected void onPause() { + super.onPause(); + Application.getInstance().removeUIListener(OnChatChangedListener.class, this); + Application.getInstance().removeUIListener(OnContactChangedListener.class, this); + Application.getInstance().removeUIListener(OnAccountChangedListener.class, this); + MessageManager.getInstance().removeVisibleChat(); + isVisible = false; + } + + private void selectChatPage(BaseEntity chat, boolean smoothScroll) { + selectPage(chatViewerAdapter.getPageNumber(chat), smoothScroll); + } + + private void selectRecentChatsPage() { + selectPage(chatViewerAdapter.getPageNumber(null), false); + } + + private void selectPage(int position, boolean smoothScroll) { + onPageSelected(position); + viewPager.setCurrentItem(position, smoothScroll); + } + + @Override + public void onChatChanged(final String account, final String user, final boolean incoming) { + if (chatViewerAdapter.updateChats()) { + chatScrollIndicatorAdapter.update(chatViewerAdapter.getActiveChats()); + selectPage(); + } else { + for (ChatViewerFragment chat : registeredChats) { + if (chat.isEqual(selectedChat)) { + chat.updateChat(); + if (incoming) { + chat.playIncomingAnimation(); + } + } + } + updateRegisteredRecentChatsFragments(); + updateStatusBar(); + } + } + + @Override + public void onContactsChanged(Collection entities) { + for (BaseEntity contact : entities) { + for (ChatViewerFragment chat : registeredChats) { + if (chat.isEqual(contact)) { + chat.updateChat(); + } + } + } + + updateRegisteredRecentChatsFragments(); + updateStatusBar(); + } + + @Override + public void onAccountsChanged(Collection accounts) { + updateRegisteredChats(); + updateRegisteredRecentChatsFragments(); + updateStatusBar(); + } + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + + } + + @Override + public void onPageSelected(int position) { + hideKeyboard(this); + chatScrollIndicatorAdapter.select(chatViewerAdapter.getRealPagePosition(position)); + + selectedChat = chatViewerAdapter.getChatByPageNumber(position); + isRecentChatsSelected = selectedChat == null; + + if (isRecentChatsSelected) { + MessageManager.getInstance().removeVisibleChat(); + } else { + if (isVisible) { + MessageManager.getInstance().setVisibleChat(selectedChat); + } + + MessageArchiveManager.getInstance().requestHistory(selectedChat.getAccount(), selectedChat.getUser(), 0, + MessageManager.getInstance().getChat(selectedChat.getAccount(), selectedChat.getUser()).getRequiredMessageCount()); + + NotificationManager.getInstance().removeMessageNotification(selectedChat.getAccount(), selectedChat.getUser()); + } + + updateStatusBar(); + } + + private void updateStatusBar() { + if (isRecentChatsSelected) { + statusBarPainter.updateWithDefaultColor(); + } else { + statusBarPainter.updateWithAccountName(selectedChat.getAccount()); + } + } + + private void updateRegisteredChats() { + for (ChatViewerFragment chat : registeredChats) { + chat.updateChat(); + } + } + + private void updateRegisteredRecentChatsFragments() { + for (RecentChatFragment recentChatFragment : recentChatFragments) { + recentChatFragment.updateChats(chatViewerAdapter.getActiveChats()); + } + } + + @Override + public void onPageScrollStateChanged(int state) { + } + + public void registerRecentChatsList(RecentChatFragment recentChatFragment) { + recentChatFragments.add(recentChatFragment); + } + + public void unregisterRecentChatsList(RecentChatFragment recentChatFragment) { + recentChatFragments.remove(recentChatFragment); + } + + @Override + public void onChatViewAdapterFinishUpdate() { + insertExtraText(); + } + + private void insertExtraText() { + + if (extraText == null) { + return; + } + + boolean isExtraTextInserted = false; + + for (ChatViewerFragment chat : registeredChats) { + if (chat.isEqual(selectedChat)) { + chat.setInputText(extraText); + isExtraTextInserted = true; + } + } + + if (isExtraTextInserted) { + extraText = null; + } + } + + @Override + public void onChatSelected(AbstractChat chat) { + selectChatPage(chat, true); + } + + public ChatViewerAdapter getChatViewerAdapter() { + return chatViewerAdapter; + } + + @Override + public void onCloseChat() { + close(); + } + + @Override + public void onMessageSent() { + if (exitOnSend) { + close(); + } + } + + @Override + public void registerChat(ChatViewerFragment chat) { + registeredChats.add(chat); + } + + @Override + public void unregisterChat(ChatViewerFragment chat) { + registeredChats.remove(chat); + } + + private void close() { + finish(); + if (!Intent.ACTION_SEND.equals(getIntent().getAction())) { + ActivityManager.getInstance().clearStack(false); + if (!ActivityManager.getInstance().hasContactList(this)) { + startActivity(ContactList.createIntent(this)); + } + } + } } diff --git a/app/src/main/java/com/xabber/android/ui/ChatViewerFragment.java b/app/src/main/java/com/xabber/android/ui/ChatViewerFragment.java new file mode 100644 index 0000000000..973a70656e --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/ChatViewerFragment.java @@ -0,0 +1,631 @@ +package com.xabber.android.ui; + +import android.app.Activity; +import android.app.Fragment; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.os.Bundle; +import android.support.v4.app.NavUtils; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.Toolbar; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.widget.EditText; +import android.widget.ImageButton; +import android.widget.PopupMenu; + +import com.xabber.android.R; +import com.xabber.android.data.Application; +import com.xabber.android.data.NetworkException; +import com.xabber.android.data.SettingsManager; +import com.xabber.android.data.account.AccountManager; +import com.xabber.android.data.entity.BaseEntity; +import com.xabber.android.data.extension.archive.MessageArchiveManager; +import com.xabber.android.data.extension.attention.AttentionManager; +import com.xabber.android.data.extension.cs.ChatStateManager; +import com.xabber.android.data.extension.muc.MUCManager; +import com.xabber.android.data.extension.muc.RoomChat; +import com.xabber.android.data.extension.muc.RoomState; +import com.xabber.android.data.extension.otr.OTRManager; +import com.xabber.android.data.extension.otr.SecurityLevel; +import com.xabber.android.data.message.AbstractChat; +import com.xabber.android.data.message.MessageItem; +import com.xabber.android.data.message.MessageManager; +import com.xabber.android.data.message.RegularChat; +import com.xabber.android.data.message.chat.ChatManager; +import com.xabber.android.data.notification.NotificationManager; +import com.xabber.android.data.roster.AbstractContact; +import com.xabber.android.data.roster.RosterManager; +import com.xabber.android.ui.adapter.ChatMessageAdapter; +import com.xabber.android.ui.dialog.ChatExportDialogFragment; +import com.xabber.android.ui.helper.AccountPainter; +import com.xabber.android.ui.helper.ContactTitleInflater; +import com.xabber.android.ui.preferences.ChatContactSettings; + +public class ChatViewerFragment extends Fragment implements PopupMenu.OnMenuItemClickListener, + View.OnClickListener, Toolbar.OnMenuItemClickListener, ChatMessageAdapter.Message.MessageClickListener { + + public static final String ARGUMENT_ACCOUNT = "ARGUMENT_ACCOUNT"; + public static final String ARGUMENT_USER = "ARGUMENT_USER"; + + private static final int MINIMUM_MESSAGES_TO_LOAD = 10; + boolean isInputEmpty = true; + private EditText inputView; + private ChatMessageAdapter chatMessageAdapter; + private boolean skipOnTextChanges = false; + private String account; + private String user; + private ImageButton sendButton; + private ImageButton securityButton; + private Toolbar toolbar; + + private ChatViewerFragmentListener listener; + private Animation shakeAnimation = null; + private RecyclerView recyclerView; + private View contactTitleView; + private AbstractContact abstractContact; + private LinearLayoutManager layoutManager; + private MessageItem clickedMessageItem; + private AccountPainter accountPainter; + + public static ChatViewerFragment newInstance(String account, String user) { + ChatViewerFragment fragment = new ChatViewerFragment(); + + Bundle arguments = new Bundle(); + arguments.putString(ARGUMENT_ACCOUNT, account); + arguments.putString(ARGUMENT_USER, user); + fragment.setArguments(arguments); + return fragment; + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + + try { + listener = (ChatViewerFragmentListener) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity.toString() + + " must implement ChatViewerFragmentListener"); + } + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Bundle args = getArguments(); + account = args.getString(ARGUMENT_ACCOUNT, null); + user = args.getString(ARGUMENT_USER, null); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + super.onCreateView(inflater, container, savedInstanceState); + + View view = inflater.inflate(R.layout.chat_viewer_fragment, container, false); + + + contactTitleView = view.findViewById(R.id.contact_title); + + abstractContact = RosterManager.getInstance().getBestContact(account, user); + contactTitleView.findViewById(R.id.avatar).setOnClickListener(this); + + toolbar = (Toolbar) view.findViewById(R.id.toolbar_default); + toolbar.inflateMenu(R.menu.chat); + toolbar.setOnMenuItemClickListener(this); + toolbar.setNavigationIcon(R.drawable.ic_arrow_left_white_24dp); + toolbar.setNavigationOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + NavUtils.navigateUpFromSameTask(getActivity()); + } + }); + + setHasOptionsMenu(true); + + accountPainter = new AccountPainter(getActivity()); + + sendButton = (ImageButton) view.findViewById(R.id.button_send_message); + sendButton.setImageResource(R.drawable.ic_button_send_inactive_24dp); + + AbstractChat abstractChat = MessageManager.getInstance().getChat(account, user); + + securityButton = (ImageButton) view.findViewById(R.id.button_security); + if (abstractChat instanceof RegularChat) { + securityButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + showSecurityMenu(); + } + }); + } else { + securityButton.setVisibility(View.GONE); + } + + chatMessageAdapter = new ChatMessageAdapter(getActivity(), account, user, this); + + recyclerView = (RecyclerView) view.findViewById(R.id.chat_messages_recycler_view); + recyclerView.setAdapter(chatMessageAdapter); + + layoutManager = new LinearLayoutManager(getActivity()); + layoutManager.setStackFromEnd(true); + recyclerView.setLayoutManager(layoutManager); + + inputView = (EditText) view.findViewById(R.id.chat_input); + + view.findViewById(R.id.button_send_message).setOnClickListener( + new View.OnClickListener() { + + @Override + public void onClick(View v) { + sendMessage(); + } + + }); + + inputView.setOnKeyListener(new View.OnKeyListener() { + @Override + public boolean onKey(View view, int keyCode, KeyEvent event) { + if (SettingsManager.chatsSendByEnter() + && event.getAction() == KeyEvent.ACTION_DOWN + && keyCode == KeyEvent.KEYCODE_ENTER) { + sendMessage(); + return true; + } + return false; + } + }); + + inputView.addTextChangedListener(new TextWatcher() { + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void afterTextChanged(Editable text) { + setSendButtonColor(); + + if (!skipOnTextChanges) { + ChatStateManager.getInstance().onComposing(account, user, text); + } + } + + }); + + return view; + } + + @Override + public void onResume() { + super.onResume(); + listener.registerChat(this); + updateChat(); + restoreInputState(); + } + + + @Override + public void onDetach() { + super.onDetach(); + listener = null; + } + + private void showSecurityMenu() { + PopupMenu popup = new PopupMenu(getActivity(), securityButton); + popup.inflate(R.menu.security); + popup.setOnMenuItemClickListener(this); + + Menu menu = popup.getMenu(); + + SecurityLevel securityLevel = OTRManager.getInstance().getSecurityLevel(account, user); + + if (securityLevel == SecurityLevel.plain) { + menu.findItem(R.id.action_start_encryption).setVisible(true) + .setEnabled(SettingsManager.securityOtrMode() != SettingsManager.SecurityOtrMode.disabled); + } else { + menu.findItem(R.id.action_restart_encryption).setVisible(true); + } + + boolean isEncrypted = securityLevel != SecurityLevel.plain; + + menu.findItem(R.id.action_stop_encryption).setEnabled(isEncrypted); + menu.findItem(R.id.action_verify_with_fingerprint).setEnabled(isEncrypted); + menu.findItem(R.id.action_verify_with_question).setEnabled(isEncrypted); + menu.findItem(R.id.action_verify_with_shared_secret).setEnabled(isEncrypted); + + popup.show(); + } + + private void setSendButtonColor() { + boolean empty = inputView.getText().toString().trim().isEmpty(); + + if (empty != isInputEmpty) { + isInputEmpty = empty; + + if (isInputEmpty) { + sendButton.setImageResource(R.drawable.ic_button_send_inactive_24dp); + } else { + sendButton.setImageResource(R.drawable.ic_button_send); + sendButton.setImageLevel(AccountManager.getInstance().getColorLevel(account)); + } + } + } + + + public void restoreInputState() { + skipOnTextChanges = true; + + inputView.setText(ChatManager.getInstance().getTypedMessage(account, user)); + inputView.setSelection(ChatManager.getInstance().getSelectionStart(account, user), + ChatManager.getInstance().getSelectionEnd(account, user)); + + skipOnTextChanges = false; + + if (!inputView.getText().toString().isEmpty()) { + inputView.requestFocus(); + } + } + + @Override + public void onPause() { + super.onPause(); + saveInputState(); + listener.unregisterChat(this); + } + + public void saveInputState() { + ChatManager.getInstance().setTyped(account, user, inputView.getText().toString(), + inputView.getSelectionStart(), inputView.getSelectionEnd()); + } + + private void sendMessage() { + String text = inputView.getText().toString().trim(); + + if (text.isEmpty()) { + return; + } + + clearInputText(); + + sendMessage(text); + + listener.onMessageSent(); + + if (SettingsManager.chatsHideKeyboard() == SettingsManager.ChatsHideKeyboard.always + || (getActivity().getResources().getBoolean(R.bool.landscape) + && SettingsManager.chatsHideKeyboard() == SettingsManager.ChatsHideKeyboard.landscape)) { + ChatViewer.hideKeyboard(getActivity()); + } + } + + private void sendMessage(String text) { + MessageManager.getInstance().sendMessage(account, user, text); + updateChat(); + } + + /** + * This method used for hardware menu button + */ + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.chat, menu); + setUpOptionsMenu(menu); + } + + /** + * This method used for hardware menu button + */ + @Override + public boolean onOptionsItemSelected(MenuItem item) { + return onMenuItemClick(item); + } + + private void setUpOptionsMenu(Menu menu) { + AbstractChat abstractChat = MessageManager.getInstance().getChat(account, user); + + if (abstractChat instanceof RoomChat) { + RoomState chatState = ((RoomChat) abstractChat).getState(); + + if (chatState == RoomState.available) { + menu.findItem(R.id.action_list_of_occupants).setVisible(true); + } + + if (chatState == RoomState.unavailable) { + menu.findItem(R.id.action_join_conference).setVisible(true); + + } else { + menu.findItem(R.id.action_invite_to_chat).setVisible(true); + + if (chatState == RoomState.error) { + menu.findItem(R.id.action_authorization_settings).setVisible(true); + } else { + menu.findItem(R.id.action_leave_conference).setVisible(true); + } + } + } + + if (abstractChat instanceof RegularChat) { + menu.findItem(R.id.action_view_contact).setVisible(true); + menu.findItem(R.id.action_close_chat).setVisible(true); + } + } + + public void updateChat() { + ContactTitleInflater.updateTitle(contactTitleView, getActivity(), abstractContact); + toolbar.setBackgroundColor(accountPainter.getAccountMainColor(account)); + int itemCountBeforeUpdate = chatMessageAdapter.getItemCount(); + chatMessageAdapter.onChange(); + scrollChat(itemCountBeforeUpdate); + setUpOptionsMenu(toolbar.getMenu()); + updateSecurityButton(); + } + + private void scrollChat(int itemCountBeforeUpdate) { + int lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition(); + if (lastVisibleItemPosition == -1 || lastVisibleItemPosition == (itemCountBeforeUpdate - 1)) { + scrollDown(); + } + } + + private void scrollDown() { + recyclerView.scrollToPosition(chatMessageAdapter.getItemCount() - 1); + } + + private void updateSecurityButton() { + SecurityLevel securityLevel = OTRManager.getInstance().getSecurityLevel(account, user); + securityButton.setImageLevel(securityLevel.getImageLevel()); + } + + public boolean isEqual(BaseEntity chat) { + return chat != null && this.account.equals(chat.getAccount()) && this.user.equals(chat.getUser()); + } + + public void setInputText(String additional) { + skipOnTextChanges = true; + inputView.setText(additional); + inputView.setSelection(additional.length()); + skipOnTextChanges = false; + } + + public String getAccount() { + return account; + } + + public String getUser() { + return user; + } + + private void clearInputText() { + skipOnTextChanges = true; + inputView.getText().clear(); + skipOnTextChanges = false; + } + + @Override + public boolean onMenuItemClick(MenuItem item) { + switch (item.getItemId()) { + /* security menu */ + + case R.id.action_start_encryption: + startEncryption(account, user); + return true; + + case R.id.action_restart_encryption: + restartEncryption(account, user); + return true; + + case R.id.action_stop_encryption: + stopEncryption(account, user); + return true; + + case R.id.action_verify_with_fingerprint: + startActivity(FingerprintViewer.createIntent(getActivity(), account, user)); + return true; + + case R.id.action_verify_with_question: + startActivity(QuestionViewer.createIntent(getActivity(), account, user, true, false, null)); + return true; + + case R.id.action_verify_with_shared_secret: + startActivity(QuestionViewer.createIntent(getActivity(), account, user, false, false, null)); + return true; + + /* regular chat options menu */ + + case R.id.action_view_contact: + showContactInfo(); + return true; + + case R.id.action_chat_settings: + startActivity(ChatContactSettings.createIntent(getActivity(), account, user)); + return true; + + case R.id.action_show_history: + showHistory(account, user); + return true; + + case R.id.action_authorization_settings: + startActivity(ConferenceAdd.createIntent(getActivity(), account, user)); + return true; + + case R.id.action_close_chat: + closeChat(account, user); + return true; + + case R.id.action_clear_history: + clearHistory(account, user); + return true; + + case R.id.action_export_chat: + ChatExportDialogFragment.newInstance(account, user).show(getFragmentManager(), "CHAT_EXPORT"); + return true; + + case R.id.action_call_attention: + callAttention(); + return true; + + /* conference specific options menu */ + + case R.id.action_join_conference: + MUCManager.getInstance().joinRoom(account, user, true); + return true; + + case R.id.action_invite_to_chat: + startActivity(ContactList.createRoomInviteIntent(getActivity(), account, user)); + return true; + + case R.id.action_leave_conference: + leaveConference(account, user); + return true; + + case R.id.action_list_of_occupants: + startActivity(OccupantList.createIntent(getActivity(), account, user)); + return true; + + /* message popup menu */ + + case R.id.action_message_repeat: + sendMessage(clickedMessageItem.getText()); + return true; + + case R.id.action_message_copy: + ((ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE)) + .setPrimaryClip(ClipData.newPlainText(clickedMessageItem.getSpannable(), clickedMessageItem.getSpannable())); + return true; + + case R.id.action_message_quote: + setInputText("> " + clickedMessageItem.getText() + "\n"); + return true; + + case R.id.action_message_remove: + MessageManager.getInstance().removeMessage(clickedMessageItem); + updateChat(); + return true; + + default: + return false; + } + } + + private void showHistory(String account, String user) { + MessageManager.getInstance().requestToLoadLocalHistory(account, user); + MessageArchiveManager.getInstance().requestHistory(account, user, MINIMUM_MESSAGES_TO_LOAD, 0); + } + + private void stopEncryption(String account, String user) { + try { + OTRManager.getInstance().endSession(account, user); + } catch (NetworkException e) { + Application.getInstance().onError(e); + } + } + + private void restartEncryption(String account, String user) { + try { + OTRManager.getInstance().refreshSession(account, user); + } catch (NetworkException e) { + Application.getInstance().onError(e); + } + } + + private void startEncryption(String account, String user) { + try { + OTRManager.getInstance().startSession(account, user); + } catch (NetworkException e) { + Application.getInstance().onError(e); + } + } + + @Override + public void onClick(View v) { + if (v.getId() == R.id.avatar) { + showContactInfo(); + } + } + + private void showContactInfo() { + if (MUCManager.getInstance().hasRoom(account, user)) { + startActivity(ContactViewer.createIntent(getActivity(), account, user)); + } else { + startActivity(ContactEditor.createIntent(getActivity(), account, user)); + } + } + + private void closeChat(String account, String user) { + MessageManager.getInstance().closeChat(account, user); + NotificationManager.getInstance().removeMessageNotification(account, user); + listener.onCloseChat(); + } + + private void clearHistory(String account, String user) { + MessageManager.getInstance().clearHistory(account, user); + } + + private void leaveConference(String account, String user) { + MUCManager.getInstance().leaveRoom(account, user); + closeChat(account, user); + } + + private void callAttention() { + try { + AttentionManager.getInstance().sendAttention(account, user); + } catch (NetworkException e) { + Application.getInstance().onError(e); + } + } + + @Override + public void onMessageClick(View caller, int position) { + int itemViewType = chatMessageAdapter.getItemViewType(position); + + if (itemViewType == ChatMessageAdapter.VIEW_TYPE_INCOMING_MESSAGE + || itemViewType == ChatMessageAdapter.VIEW_TYPE_OUTGOING_MESSAGE) { + + clickedMessageItem = chatMessageAdapter.getMessageItem(position); + + PopupMenu popup = new PopupMenu(getActivity(), caller); + popup.inflate(R.menu.chat_context_menu); + popup.setOnMenuItemClickListener(this); + + if (chatMessageAdapter.getMessageItem(position).isError()) { + popup.getMenu().findItem(R.id.action_message_repeat).setVisible(true); + } + + popup.show(); + } + } + + public void playIncomingAnimation() { + if (shakeAnimation == null) { + shakeAnimation = AnimationUtils.loadAnimation(getActivity(), R.anim.shake); + } + toolbar.findViewById(R.id.name_holder).startAnimation(shakeAnimation); + } + + public interface ChatViewerFragmentListener { + void onCloseChat(); + + void onMessageSent(); + + void registerChat(ChatViewerFragment chat); + + void unregisterChat(ChatViewerFragment chat); + } +} diff --git a/app/src/main/java/com/xabber/android/ui/ClearNotifications.java b/app/src/main/java/com/xabber/android/ui/ClearNotifications.java index b4daf01d6a..e85f45193e 100644 --- a/app/src/main/java/com/xabber/android/ui/ClearNotifications.java +++ b/app/src/main/java/com/xabber/android/ui/ClearNotifications.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -25,27 +25,26 @@ /** * Activity to clear all notifications. - * + * * @author alexander.ivanov - * */ public class ClearNotifications extends Activity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - LogManager.i(this, "onCreate"); - if (Application.getInstance().isInitialized()) - NotificationManager.getInstance().onClearNotifications(); - finish(); - } + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + LogManager.i(this, "onCreate"); + if (Application.getInstance().isInitialized()) + NotificationManager.getInstance().onClearNotifications(); + finish(); + } - public static Intent createIntent(Context context) { - Intent intent = new Intent(context, ClearNotifications.class); - intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION - | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP - | Intent.FLAG_ACTIVITY_NEW_TASK); - return intent; - } + public static Intent createIntent(Context context) { + Intent intent = new Intent(context, ClearNotifications.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION + | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP + | Intent.FLAG_ACTIVITY_NEW_TASK); + return intent; + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/ui/ConferenceAdd.java b/app/src/main/java/com/xabber/android/ui/ConferenceAdd.java new file mode 100644 index 0000000000..2beb8b27fb --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/ConferenceAdd.java @@ -0,0 +1,119 @@ +/** + * Copyright (c) 2013, Redsolution LTD. All rights reserved. + * + * This file is part of Xabber project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License, Version 3. + * + * Xabber is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License, + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.xabber.android.ui; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.widget.Toolbar; + +import com.xabber.android.R; +import com.xabber.android.data.account.AccountManager; +import com.xabber.android.data.intent.AccountIntentBuilder; +import com.xabber.android.data.intent.EntityIntentBuilder; +import com.xabber.android.ui.helper.BarPainter; +import com.xabber.android.ui.helper.ManagedActivity; + +import java.util.Collection; + +public class ConferenceAdd extends ManagedActivity implements ConferenceAddFragment.Listener { + + private static final String SAVED_ACCOUNT = "com.xabber.android.ui.MUCEditor.SAVED_ACCOUNT"; + private static final String SAVED_ROOM = "com.xabber.android.ui.MUCEditor.SAVED_ROOM"; + + private BarPainter barPainter; + private String account; + private String room; + + public static Intent createIntent(Context context) { + return ConferenceAdd.createIntent(context, null, null); + } + + public static Intent createIntent(Context context, String account, + String room) { + return new EntityIntentBuilder(context, ConferenceAdd.class).setAccount(account).setUser(room).build(); + } + + private static String getAccount(Intent intent) { + return AccountIntentBuilder.getAccount(intent); + } + + private static String getUser(Intent intent) { + return EntityIntentBuilder.getUser(intent); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (isFinishing()) { + return; + } + + setContentView(R.layout.activity_with_toolbar_and_container); + + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_default); + toolbar.setNavigationIcon(R.drawable.ic_clear_white_24dp); + setTitle(null); + + setSupportActionBar(toolbar); + + barPainter = new BarPainter(this, toolbar); + barPainter.setDefaultColor(); + + Intent intent = getIntent(); + + account = null; + room = null; + + if (savedInstanceState != null) { + account = savedInstanceState.getString(SAVED_ACCOUNT); + room = savedInstanceState.getString(SAVED_ROOM); + } else { + account = getAccount(intent); + room = getUser(intent); + } + + if (account == null) { + Collection accounts = AccountManager.getInstance().getAccounts(); + if (accounts.size() == 1) { + account = accounts.iterator().next(); + } + } + + if (account != null) { + barPainter.updateWithAccountName(account); + } + + if (savedInstanceState == null) { + getFragmentManager() + .beginTransaction() + .add(R.id.fragment_container, ConferenceAddFragment.newInstance(account, room)) + .commit(); + } + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putString(SAVED_ACCOUNT, account); + outState.putString(SAVED_ROOM, room); + } + + @Override + public void onAccountSelected(String account) { + barPainter.updateWithAccountName(account); + this.account = account; + } +} diff --git a/app/src/main/java/com/xabber/android/ui/ConferenceAddFragment.java b/app/src/main/java/com/xabber/android/ui/ConferenceAddFragment.java new file mode 100644 index 0000000000..4a041a96bf --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/ConferenceAddFragment.java @@ -0,0 +1,231 @@ +package com.xabber.android.ui; + +import android.app.Activity; +import android.app.Fragment; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.Spinner; +import android.widget.Toast; + +import com.xabber.android.R; +import com.xabber.android.data.account.AccountManager; +import com.xabber.android.data.extension.muc.MUCManager; +import com.xabber.android.data.extension.muc.RoomInvite; +import com.xabber.android.data.message.MessageManager; +import com.xabber.android.data.notification.NotificationManager; +import com.xabber.android.ui.adapter.AccountChooseAdapter; + +import org.jivesoftware.smack.util.StringUtils; + +public class ConferenceAddFragment extends Fragment implements AdapterView.OnItemSelectedListener { + + protected static final String ARG_ACCOUNT = "com.xabber.android.ui.ConferenceAddFragment.ARG_ACCOUNT"; + protected static final String ARG_ROOM = "com.xabber.android.ui.ConferenceAddFragment.ARG_ROOM"; + + private Spinner accountView; + private EditText serverView; + private EditText roomView; + private EditText nickView; + private EditText passwordView; + private CheckBox joinCheckBox; + + private int selectedAccount; + + private String account = null; + private String room = null; + + private Listener listener; + + public static ConferenceAddFragment newInstance(String account, String room) { + ConferenceAddFragment fragment = new ConferenceAddFragment(); + Bundle args = new Bundle(); + args.putString(ARG_ACCOUNT, account); + args.putString(ARG_ROOM, room); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + + listener = (Listener) activity; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (getArguments() != null) { + account = getArguments().getString(ARG_ACCOUNT); + room = getArguments().getString(ARG_ROOM); + } + } + + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.conference_add_fragment, container, false); + + accountView = (Spinner) view.findViewById(R.id.contact_account); + serverView = (EditText) view.findViewById(R.id.muc_server); + roomView = (EditText) view.findViewById(R.id.muc_room); + nickView = (EditText) view.findViewById(R.id.muc_nick); + passwordView = (EditText) view.findViewById(R.id.muc_password); + joinCheckBox = (CheckBox) view.findViewById(R.id.muc_join); + + accountView.setAdapter(new AccountChooseAdapter(getActivity())); + accountView.setOnItemSelectedListener(this); + + if (room != null) { + serverView.setText(StringUtils.parseServer(room)); + roomView.setText(StringUtils.parseName(room)); + } + + if (account != null && room != null) { + MUCManager.getInstance().removeAuthorizationError(account, room); + nickView.setText(MUCManager.getInstance().getNickname(account, room)); + String password; + RoomInvite roomInvite = MUCManager.getInstance().getInvite(account, room); + if (roomInvite != null) { + password = roomInvite.getPassword(); + } else { + password = MUCManager.getInstance().getPassword(account, room); + } + passwordView.setText(password); + } + + if (account != null) { + for (int position = 0; position < accountView.getCount(); position++) { + if (account.equals(accountView.getItemAtPosition(position))) { + accountView.setSelection(position); + break; + } + } + } + if ("".equals(nickView.getText().toString())) { + nickView.setText(getNickname(((String) accountView.getSelectedItem()))); + } + + setHasOptionsMenu(true); + + return view; + } + + @Override + public void onResume() { + super.onResume(); + selectedAccount = accountView.getSelectedItemPosition(); + } + + @Override + public void onDetach() { + super.onDetach(); + listener = null; + } + + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + String current = nickView.getText().toString(); + String previous; + if (selectedAccount == AdapterView.INVALID_POSITION) { + previous = ""; + } else { + previous = getNickname((String) accountView.getAdapter().getItem(selectedAccount)); + } + if (current.equals(previous)) { + nickView.setText(getNickname((String) accountView.getSelectedItem())); + } + selectedAccount = accountView.getSelectedItemPosition(); + + listener.onAccountSelected((String) accountView.getSelectedItem()); + } + + @Override + public void onNothingSelected(AdapterView parent) { + selectedAccount = accountView.getSelectedItemPosition(); + } + + /** + * @param account + * @return Suggested nickname in the room. + */ + private String getNickname(String account) { + if (account == null) { + return ""; + } + String nickname = AccountManager.getInstance().getNickName(account); + String name = StringUtils.parseName(nickname); + if ("".equals(name)) { + return nickname; + } else { + return name; + } + } + + private void addConference() { + String account = (String) accountView.getSelectedItem(); + if (account == null) { + Toast.makeText(getActivity(), getString(R.string.EMPTY_ACCOUNT), Toast.LENGTH_LONG).show(); + return; + } + String server = serverView.getText().toString(); + if ("".equals(server)) { + Toast.makeText(getActivity(), getString(R.string.EMPTY_SERVER_NAME), Toast.LENGTH_LONG).show(); + return; + } + String room = roomView.getText().toString(); + if ("".equals(room)) { + Toast.makeText(getActivity(), getString(R.string.EMPTY_ROOM_NAME), Toast.LENGTH_LONG).show(); + return; + } + String nick = nickView.getText().toString(); + if ("".equals(nick)) { + Toast.makeText(getActivity(), getString(R.string.EMPTY_NICK_NAME), Toast.LENGTH_LONG).show(); + return; + } + String password = passwordView.getText().toString(); + boolean join = joinCheckBox.isChecked(); + room = room + "@" + server; + if (this.account != null && this.room != null) { + if (!account.equals(this.account) || !room.equals(this.room)) { + MUCManager.getInstance().removeRoom(this.account, this.room); + MessageManager.getInstance().closeChat(this.account, this.room); + NotificationManager.getInstance().removeMessageNotification(this.account, this.room); + } + } + MUCManager.getInstance().createRoom(account, room, nick, password, join); + getActivity().finish(); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.add_conference, menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_add_conference: + addConference(); + return true; + + default: + return super.onOptionsItemSelected(item); + } + } + + interface Listener { + void onAccountSelected(String account); + } +} diff --git a/app/src/main/java/com/xabber/android/ui/ContactAdd.java b/app/src/main/java/com/xabber/android/ui/ContactAdd.java index a265a3fac1..af326394a9 100644 --- a/app/src/main/java/com/xabber/android/ui/ContactAdd.java +++ b/app/src/main/java/com/xabber/android/ui/ContactAdd.java @@ -1,311 +1,107 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.ui; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; - -import android.app.Dialog; import android.content.Context; import android.content.Intent; import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemSelectedListener; -import android.widget.Button; -import android.widget.EditText; -import android.widget.ListView; -import android.widget.Spinner; -import android.widget.Toast; +import android.support.v7.widget.Toolbar; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; -import com.xabber.android.data.Application; -import com.xabber.android.data.NetworkException; -import com.xabber.android.data.account.AccountManager; +import com.xabber.android.R; import com.xabber.android.data.intent.EntityIntentBuilder; -import com.xabber.android.data.message.MessageManager; -import com.xabber.android.data.roster.PresenceManager; -import com.xabber.android.data.roster.RosterManager; -import com.xabber.android.data.roster.SubscriptionRequest; -import com.xabber.android.ui.adapter.AccountChooseAdapter; -import com.xabber.android.ui.dialog.ConfirmDialogBuilder; -import com.xabber.android.ui.dialog.ConfirmDialogListener; -import com.xabber.android.ui.dialog.DialogBuilder; -import com.xabber.androiddev.R; - -public class ContactAdd extends GroupListActivity implements - View.OnClickListener, ConfirmDialogListener, OnItemSelectedListener { - - /** - * Action for subscription request to be show. - * - * Clear action on dialog dismiss. - */ - private static final String ACTION_SUBSCRIPTION_REQUEST = "com.xabber.android.data.SUBSCRIPTION_REQUEST"; - - private static final String SAVED_ACCOUNT = "com.xabber.android.ui.ContactAdd.SAVED_ACCOUNT"; - private static final String SAVED_USER = "com.xabber.android.ui.ContactAdd.SAVED_USER"; - private static final String SAVED_NAME = "com.xabber.android.ui.ContactAdd.SAVED_NAME"; - - private static final int DIALOG_SUBSCRIPTION_REQUEST_ID = 0x20; - - private String account; - private String user; - - private SubscriptionRequest subscriptionRequest; - - /** - * Views - */ - private Spinner accountView; - private EditText userView; - private EditText nameView; - - @Override - protected void onInflate(Bundle savedInstanceState) { - setContentView(R.layout.contact_add); - - ListView listView = getListView(); - LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE); - View view = inflater.inflate(R.layout.contact_add_header, listView, - false); - listView.addHeaderView(view, null, false); +import com.xabber.android.ui.helper.BarPainter; +import com.xabber.android.ui.helper.ManagedActivity; - accountView = (Spinner) view.findViewById(R.id.contact_account); - accountView.setAdapter(new AccountChooseAdapter(this)); - accountView.setOnItemSelectedListener(this); - userView = (EditText) view.findViewById(R.id.contact_user); - nameView = (EditText) view.findViewById(R.id.contact_name); - ((Button) view.findViewById(R.id.ok)).setOnClickListener(this); +public class ContactAdd extends ManagedActivity implements ContactAddFragment.Listener { - String name; - Intent intent = getIntent(); - if (savedInstanceState != null) { - account = savedInstanceState.getString(SAVED_ACCOUNT); - user = savedInstanceState.getString(SAVED_USER); - name = savedInstanceState.getString(SAVED_NAME); - } else { - account = getAccount(intent); - user = getUser(intent); - if (account == null || user == null) - name = null; - else { - name = RosterManager.getInstance().getName(account, user); - if (user.equals(name)) - name = null; - } - } - if (account == null) { - Collection accounts = AccountManager.getInstance() - .getAccounts(); - if (accounts.size() == 1) - account = accounts.iterator().next(); - } - if (account != null) { - for (int position = 0; position < accountView.getCount(); position++) - if (account.equals(accountView.getItemAtPosition(position))) { - accountView.setSelection(position); - break; - } - } - if (user != null) - userView.setText(user); - if (name != null) - nameView.setText(name); - if (ACTION_SUBSCRIPTION_REQUEST.equals(intent.getAction())) { - subscriptionRequest = PresenceManager.getInstance() - .getSubscriptionRequest(account, user); - if (subscriptionRequest == null) { - Application.getInstance().onError(R.string.ENTRY_IS_NOT_FOUND); - finish(); - return; - } - } else { - subscriptionRequest = null; - } - } + BarPainter barPainter; - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putString(SAVED_ACCOUNT, - (String) accountView.getSelectedItem()); - outState.putString(SAVED_USER, userView.getText().toString()); - outState.putString(SAVED_NAME, nameView.getText().toString()); - } + public static Intent createIntent(Context context) { + return createIntent(context, null); + } - @Override - protected void onResume() { - super.onResume(); - if (subscriptionRequest != null) - showDialog(DIALOG_SUBSCRIPTION_REQUEST_ID); - } + public static Intent createIntent(Context context, String account) { + return createIntent(context, account, null); + } - @Override - public void onClick(View view) { - switch (view.getId()) { - case R.id.ok: - String user = userView.getText().toString(); - if ("".equals(user)) { - Toast.makeText(this, getString(R.string.EMPTY_USER_NAME), - Toast.LENGTH_LONG).show(); - return; - } - String account = (String) accountView.getSelectedItem(); - if (account == null) { - Toast.makeText(this, getString(R.string.EMPTY_ACCOUNT), - Toast.LENGTH_LONG).show(); - return; - } - try { - RosterManager.getInstance().createContact(account, user, - nameView.getText().toString(), getSelected()); - PresenceManager.getInstance() - .requestSubscription(account, user); - } catch (NetworkException e) { - Application.getInstance().onError(e); - finish(); - return; - } - MessageManager.getInstance().openChat(account, user); - finish(); - break; - default: - break; - } - } + public static Intent createIntent(Context context, String account, String user) { + return new EntityIntentBuilder(context, ContactAdd.class).setAccount(account).setUser(user).build(); + } - @Override - protected Dialog onCreateDialog(int id) { - Dialog dialog = super.onCreateDialog(id); - if (dialog != null) - return dialog; - switch (id) { - case DIALOG_SUBSCRIPTION_REQUEST_ID: - return new ConfirmDialogBuilder(this, - DIALOG_SUBSCRIPTION_REQUEST_ID, this).setMessage( - subscriptionRequest.getConfirmation()).create(); - default: - return null; - } - } + private static String getAccount(Intent intent) { + return EntityIntentBuilder.getAccount(intent); + } - @Override - public void onAccept(DialogBuilder dialogBuilder) { - super.onAccept(dialogBuilder); - switch (dialogBuilder.getDialogId()) { - case DIALOG_SUBSCRIPTION_REQUEST_ID: - try { - PresenceManager.getInstance().acceptSubscription( - subscriptionRequest.getAccount(), - subscriptionRequest.getUser()); - } catch (NetworkException e) { - Application.getInstance().onError(e); - } - getIntent().setAction(null); - break; - } - } + private static String getUser(Intent intent) { + return EntityIntentBuilder.getUser(intent); + } - @Override - public void onDecline(DialogBuilder dialogBuilder) { - super.onDecline(dialogBuilder); - switch (dialogBuilder.getDialogId()) { - case DIALOG_SUBSCRIPTION_REQUEST_ID: - try { - PresenceManager.getInstance().discardSubscription( - subscriptionRequest.getAccount(), - subscriptionRequest.getUser()); - } catch (NetworkException e) { - Application.getInstance().onError(e); - } - finish(); - break; - } - } + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); - @Override - public void onCancel(DialogBuilder dialogBuilder) { - super.onCancel(dialogBuilder); - switch (dialogBuilder.getDialogId()) { - case DIALOG_SUBSCRIPTION_REQUEST_ID: - finish(); - break; - } - } + setContentView(R.layout.activity_with_toolbar_and_container); + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_default); + toolbar.setNavigationIcon(R.drawable.ic_clear_white_24dp); + setTitle(null); + barPainter = new BarPainter(this, toolbar); + barPainter.setDefaultColor(); - @Override - Collection getInitialGroups() { - String account = (String) accountView.getSelectedItem(); - if (account == null) - return Collections.emptyList(); - return RosterManager.getInstance().getGroups(account); - } + setSupportActionBar(toolbar); - @Override - Collection getInitialSelected() { - return Collections.emptyList(); - } - @Override - public void onItemSelected(AdapterView parent, View view, int position, - long id) { - String account = (String) accountView.getSelectedItem(); - if (account == null) { - onNothingSelected(parent); - } else { - HashSet groups = new HashSet(RosterManager - .getInstance().getGroups(account)); - groups.addAll(getSelected()); - setGroups(groups, getSelected()); - } - } + Intent intent = getIntent(); - @Override - public void onNothingSelected(AdapterView parent) { - setGroups(getSelected(), getSelected()); - } + if (savedInstanceState == null) { + getSupportFragmentManager() + .beginTransaction() + .add(R.id.fragment_container, ContactAddFragment.newInstance(getAccount(intent), getUser(intent))) + .commit(); + } - public static Intent createIntent(Context context) { - return createIntent(context, null); - } + } - private static Intent createIntent(Context context, String account, - String user) { - return new EntityIntentBuilder(context, ContactAdd.class) - .setAccount(account).setUser(user).build(); - } + private void addContact() { + ((ContactAdder) getSupportFragmentManager().findFragmentById(R.id.fragment_container)).addContact(); + } - public static Intent createIntent(Context context, String account) { - return createIntent(context, account, null); - } + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater=getMenuInflater(); + inflater.inflate(R.menu.add_contact, menu); - public static Intent createSubscriptionIntent(Context context, - String account, String user) { - Intent intent = createIntent(context, account, user); - intent.setAction(ACTION_SUBSCRIPTION_REQUEST); - return intent; - } + return true; + } - private static String getAccount(Intent intent) { - return EntityIntentBuilder.getAccount(intent); - } + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_add_contact: + addContact(); + return true; - private static String getUser(Intent intent) { - return EntityIntentBuilder.getUser(intent); - } + default: + return super.onOptionsItemSelected(item); + } + } + @Override + public void onAccountSelected(String account) { + barPainter.updateWithAccountName(account); + } } diff --git a/app/src/main/java/com/xabber/android/ui/ContactAddFragment.java b/app/src/main/java/com/xabber/android/ui/ContactAddFragment.java new file mode 100644 index 0000000000..61f0cdcdd3 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/ContactAddFragment.java @@ -0,0 +1,206 @@ +package com.xabber.android.ui; + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.EditText; +import android.widget.Spinner; +import android.widget.Toast; + +import com.xabber.android.R; +import com.xabber.android.data.Application; +import com.xabber.android.data.NetworkException; +import com.xabber.android.data.account.AccountManager; +import com.xabber.android.data.message.MessageManager; +import com.xabber.android.data.roster.PresenceManager; +import com.xabber.android.data.roster.RosterManager; +import com.xabber.android.ui.adapter.AccountChooseAdapter; + +import java.util.Collection; + +public class ContactAddFragment extends GroupEditorFragment + implements AdapterView.OnItemSelectedListener, ContactAdder { + + private static final String SAVED_NAME = "com.xabber.android.ui.ContactAdd.SAVED_NAME"; + private static final String SAVED_ACCOUNT = "com.xabber.android.ui.ContactAdd.SAVED_ACCOUNT"; + private static final String SAVED_USER = "com.xabber.android.ui.ContactAdd.SAVED_USER"; + Listener listenerActivity; + private Spinner accountView; + private EditText userView; + private EditText nameView; + private String name; + private View accountSelectorPanel; + + public static ContactAddFragment newInstance(String account, String user) { + ContactAddFragment fragment = new ContactAddFragment(); + Bundle args = new Bundle(); + args.putString(ARG_ACCOUNT, account); + args.putString(ARG_USER, user); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + listenerActivity = (Listener)activity; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.contact_add_fragment, container, false); + + + if (savedInstanceState != null) { + name = savedInstanceState.getString(SAVED_NAME); + setAccount(savedInstanceState.getString(SAVED_ACCOUNT)); + setUser(savedInstanceState.getString(SAVED_USER)); + } else { + if (getAccount() == null || getUser() == null) { + name = null; + } else { + name = RosterManager.getInstance().getName(getAccount(), getUser()); + if (getUser().equals(name)) { + name = null; + } + } + } + if (getAccount() == null) { + Collection accounts = AccountManager.getInstance().getAccounts(); + if (accounts.size() == 1) { + setAccount(accounts.iterator().next()); + } + } + + accountSelectorPanel = view.findViewById(R.id.account_selector); + + setUpAccountView((Spinner) view.findViewById(R.id.contact_account)); + + return view; + } + + private void setUpAccountView(Spinner view) { + accountView = view; + accountView.setAdapter(new AccountChooseAdapter(getActivity())); + accountView.setOnItemSelectedListener(this); + + if (getAccount() != null) { + for (int position = 0; position < accountView.getCount(); position++) { + if (getAccount().equals(accountView.getItemAtPosition(position))) { + accountView.setSelection(position); + break; + } + } + } + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + getListView().setVisibility(View.GONE); + } + + private void setUpListView() { + View headerView = ((LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE)) + .inflate(R.layout.contact_add_header, null, false); + getListView().addHeaderView(headerView); + + accountSelectorPanel.setVisibility(View.GONE); + + setUpAccountView((Spinner) headerView.findViewById(R.id.contact_account)); + + userView = (EditText) headerView.findViewById(R.id.contact_user); + nameView = (EditText) headerView.findViewById(R.id.contact_name); + + if (getUser() != null) { + userView.setText(getUser()); + } + if (name != null) { + nameView.setText(name); + } + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putString(SAVED_ACCOUNT, getAccount()); + outState.putString(SAVED_USER, userView.getText().toString()); + outState.putString(SAVED_NAME, nameView.getText().toString()); + + } + + @Override + public void onDetach() { + super.onDetach(); + listenerActivity = null; + } + + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + String selectedAccount = (String) accountView.getSelectedItem(); + + if (selectedAccount == null) { + onNothingSelected(parent); + setAccount(selectedAccount); + } else { + listenerActivity.onAccountSelected(selectedAccount); + + if (!selectedAccount.equals(getAccount())) { + setAccount(selectedAccount); + setAccountGroups(); + updateGroups(); + } + + if (getListView().getVisibility() == View.GONE) { + setUpListView(); + getListView().setVisibility(View.VISIBLE); + } + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + } + + @Override + public void addContact() { + if (getAccount() == null) { + Toast.makeText(getActivity(), getString(R.string.EMPTY_ACCOUNT), + Toast.LENGTH_LONG).show(); + return; + } + + String user = userView.getText().toString(); + if ("".equals(user)) { + Toast.makeText(getActivity(), getString(R.string.EMPTY_USER_NAME), + Toast.LENGTH_LONG).show(); + return; + } + String account = (String) accountView.getSelectedItem(); + if (account == null) { + Toast.makeText(getActivity(), getString(R.string.EMPTY_ACCOUNT), + Toast.LENGTH_LONG).show(); + return; + } + try { + RosterManager.getInstance().createContact(account, user, + nameView.getText().toString(), getSelected()); + PresenceManager.getInstance().requestSubscription(account, user); + } catch (NetworkException e) { + Application.getInstance().onError(e); + getActivity().finish(); + return; + } + MessageManager.getInstance().openChat(account, user); + getActivity().finish(); + } + + public interface Listener { + void onAccountSelected(String account); + } +} diff --git a/app/src/main/java/com/xabber/android/ui/ContactAdder.java b/app/src/main/java/com/xabber/android/ui/ContactAdder.java new file mode 100644 index 0000000000..24e61b9fe2 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/ContactAdder.java @@ -0,0 +1,5 @@ +package com.xabber.android.ui; + +public interface ContactAdder { + void addContact(); +} diff --git a/app/src/main/java/com/xabber/android/ui/ContactEditor.java b/app/src/main/java/com/xabber/android/ui/ContactEditor.java index e0de9308b7..2ad74a5f63 100644 --- a/app/src/main/java/com/xabber/android/ui/ContactEditor.java +++ b/app/src/main/java/com/xabber/android/ui/ContactEditor.java @@ -1,142 +1,108 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ package com.xabber.android.ui; -import java.util.Collection; - +import android.app.AlertDialog; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; -import android.widget.AdapterView; +import android.support.v7.widget.Toolbar; +import android.text.InputType; +import android.view.Menu; +import android.view.MenuItem; import android.widget.EditText; -import android.widget.TextView; +import com.xabber.android.R; import com.xabber.android.data.Application; import com.xabber.android.data.NetworkException; -import com.xabber.android.data.account.AccountManager; -import com.xabber.android.data.account.OnAccountChangedListener; -import com.xabber.android.data.entity.BaseEntity; import com.xabber.android.data.intent.EntityIntentBuilder; -import com.xabber.android.data.roster.AbstractContact; -import com.xabber.android.data.roster.OnContactChangedListener; +import com.xabber.android.data.roster.RosterContact; import com.xabber.android.data.roster.RosterManager; -import com.xabber.android.ui.helper.ContactTitleInflater; -import com.xabber.androiddev.R; -import com.xabber.xmpp.address.Jid; - -public class ContactEditor extends GroupListActivity implements - OnContactChangedListener, AdapterView.OnItemClickListener, - OnAccountChangedListener { - - private String account; - private String user; - - @Override - protected void onInflate(Bundle savedInstanceState) { - setContentView(R.layout.contact_editor); - - Intent intent = getIntent(); - account = ContactEditor.getAccount(intent); - user = ContactEditor.getUser(intent); - if (AccountManager.getInstance().getAccount(account) == null - || user == null) { - Application.getInstance().onError(R.string.ENTRY_IS_NOT_FOUND); - finish(); - return; - } - } - - @Override - Collection getInitialGroups() { - return RosterManager.getInstance().getGroups(account); - } - - @Override - Collection getInitialSelected() { - return RosterManager.getInstance().getGroups(account, user); - } - - @Override - protected void onResume() { - super.onResume(); - ((EditText) findViewById(R.id.contact_name)).setText(RosterManager - .getInstance().getName(account, user)); - Application.getInstance().addUIListener(OnAccountChangedListener.class, - this); - Application.getInstance().addUIListener(OnContactChangedListener.class, - this); - update(); - } - - @Override - protected void onPause() { - super.onPause(); - Application.getInstance().removeUIListener( - OnAccountChangedListener.class, this); - Application.getInstance().removeUIListener( - OnContactChangedListener.class, this); - try { - String name = ((EditText) findViewById(R.id.contact_name)) - .getText().toString(); - RosterManager.getInstance().setNameAndGroup(account, user, name, - getSelected()); - } catch (NetworkException e) { - Application.getInstance().onError(e); - } - } - - private void update() { - AbstractContact abstractContact = RosterManager.getInstance() - .getBestContact(account, user); - ContactTitleInflater.updateTitle(findViewById(R.id.title), this, - abstractContact); - ((TextView) findViewById(R.id.name)).setText(getString( - R.string.contact_editor_title, abstractContact.getName())); - ((TextView) findViewById(R.id.status_text)).setText(user); - } - - @Override - public void onContactsChanged(Collection entities) { - String thisBareAddress = Jid.getBareAddress(user); - for (BaseEntity entity : entities) - if (entity.equals(account, thisBareAddress)) { - update(); - break; - } - } - - @Override - public void onAccountsChanged(Collection accounts) { - if (accounts.contains(account)) - update(); - } - - public static Intent createIntent(Context context, String account, - String user) { - Intent intent = new EntityIntentBuilder(context, ContactEditor.class) - .setAccount(account).setUser(user).build(); - intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); - return intent; - } - - private static String getAccount(Intent intent) { - return EntityIntentBuilder.getAccount(intent); - } - - private static String getUser(Intent intent) { - return EntityIntentBuilder.getUser(intent); - } - +import com.xabber.android.ui.dialog.ContactDeleteDialogFragment; + +public class ContactEditor extends ContactViewer implements Toolbar.OnMenuItemClickListener { + + public static Intent createIntent(Context context, String account, String user) { + return new EntityIntentBuilder(context, ContactEditor.class) + .setAccount(account).setUser(user).build(); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Toolbar toolbar = contactTitleExpandableToolbarInflater.getToolbar(); + + RosterContact rosterContact = RosterManager.getInstance().getRosterContact(getAccount(), getBareAddress()); + if (rosterContact != null) { + toolbar.inflateMenu(R.menu.contact_viewer); + toolbar.setOnMenuItemClickListener(this); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + RosterContact rosterContact = RosterManager.getInstance().getRosterContact(getAccount(), getBareAddress()); + if (rosterContact != null) { + getMenuInflater().inflate(R.menu.contact_viewer, menu); + } + + return true; + } + + @Override + public boolean onMenuItemClick(MenuItem item) { + return onOptionsItemSelected(item); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_edit_alias: + editAlias(); + return true; + + case R.id.action_edit_groups: + startActivity(GroupEditor.createIntent(this, getAccount(), getBareAddress())); + return true; + + case R.id.action_remove_contact: + ContactDeleteDialogFragment.newInstance(getAccount(), getBareAddress()) + .show(getFragmentManager(), "CONTACT_DELETE"); + return true; + + default: + return super.onOptionsItemSelected(item); + } + } + + private void editAlias() { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.edit_alias); + + final EditText input = new EditText(this); + input.setInputType(InputType.TYPE_TEXT_VARIATION_PERSON_NAME); + + RosterContact rosterContact = RosterManager.getInstance().getRosterContact(getAccount(), getBareAddress()); + input.setText(rosterContact.getName()); + builder.setView(input); + + builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + try { + RosterManager.getInstance().setName(getAccount(), getBareAddress(), input.getText().toString()); + } catch (NetworkException e) { + Application.getInstance().onError(e); + } + } + }); + builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.cancel(); + } + }); + + builder.show(); + } } diff --git a/app/src/main/java/com/xabber/android/ui/ContactList.java b/app/src/main/java/com/xabber/android/ui/ContactList.java index 764f6c7fb6..4ab3472133 100644 --- a/app/src/main/java/com/xabber/android/ui/ContactList.java +++ b/app/src/main/java/com/xabber/android/ui/ContactList.java @@ -1,1158 +1,644 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.ui; -import java.util.ArrayList; -import java.util.Collection; - import android.app.Dialog; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnCancelListener; import android.content.Intent; -import android.content.res.ColorStateList; -import android.content.res.TypedArray; +import android.content.res.Configuration; import android.graphics.Bitmap; -import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.Handler; -import android.os.SystemClock; +import android.support.v4.app.Fragment; +import android.support.v4.view.MenuItemCompat; +import android.support.v4.widget.DrawerLayout; +import android.support.v7.app.ActionBarDrawerToggle; +import android.support.v7.widget.Toolbar; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; -import android.view.KeyEvent; +import android.view.Gravity; import android.view.Menu; import android.view.MenuItem; -import android.view.MotionEvent; -import android.view.SubMenu; import android.view.View; -import android.view.View.OnLongClickListener; import android.view.inputmethod.InputMethodManager; -import android.widget.AdapterView; -import android.widget.AdapterView.AdapterContextMenuInfo; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.ListView; -import android.widget.TextView; +import android.widget.SearchView; import android.widget.Toast; +import com.xabber.android.R; import com.xabber.android.data.ActivityManager; import com.xabber.android.data.Application; import com.xabber.android.data.NetworkException; import com.xabber.android.data.SettingsManager; -import com.xabber.android.data.account.AccountItem; import com.xabber.android.data.account.AccountManager; +import com.xabber.android.data.account.CommonState; import com.xabber.android.data.account.OnAccountChangedListener; -import com.xabber.android.data.account.StatusMode; -import com.xabber.android.data.connection.ConnectionManager; -import com.xabber.android.data.connection.ConnectionState; import com.xabber.android.data.entity.BaseEntity; import com.xabber.android.data.extension.avatar.AvatarManager; import com.xabber.android.data.extension.muc.MUCManager; import com.xabber.android.data.intent.EntityIntentBuilder; import com.xabber.android.data.message.AbstractChat; import com.xabber.android.data.message.MessageManager; -import com.xabber.android.data.message.OnChatChangedListener; import com.xabber.android.data.notification.NotificationManager; import com.xabber.android.data.roster.AbstractContact; -import com.xabber.android.data.roster.GroupManager; -import com.xabber.android.data.roster.OnContactChangedListener; -import com.xabber.android.data.roster.PresenceManager; import com.xabber.android.data.roster.RosterContact; import com.xabber.android.data.roster.RosterManager; -import com.xabber.android.data.roster.ShowOfflineMode; -import com.xabber.android.ui.adapter.AccountConfiguration; -import com.xabber.android.ui.adapter.AccountToggleAdapter; -import com.xabber.android.ui.adapter.ContactListAdapter; -import com.xabber.android.ui.adapter.GroupConfiguration; -import com.xabber.android.ui.dialog.AccountChooseDialogBuilder; -import com.xabber.android.ui.dialog.ConfirmDialogBuilder; -import com.xabber.android.ui.dialog.ConfirmDialogListener; -import com.xabber.android.ui.dialog.DialogBuilder; -import com.xabber.android.ui.dialog.GroupRenameDialogBuilder; -import com.xabber.android.ui.helper.ManagedListActivity; -import com.xabber.androiddev.R; +import com.xabber.android.ui.ContactListFragment.ContactListFragmentListener; +import com.xabber.android.ui.dialog.AccountChooseDialogFragment; +import com.xabber.android.ui.dialog.AccountChooseDialogFragment.OnChoosedListener; +import com.xabber.android.ui.dialog.ContactIntegrationDialogFragment; +import com.xabber.android.ui.dialog.StartAtBootDialogFragment; +import com.xabber.android.ui.dialog.TranslationDialog; +import com.xabber.android.ui.helper.BarPainter; +import com.xabber.android.ui.helper.ManagedActivity; +import com.xabber.android.ui.preferences.AccountEditor; +import com.xabber.android.ui.preferences.AccountList; +import com.xabber.android.ui.preferences.PreferenceEditor; import com.xabber.xmpp.address.Jid; import com.xabber.xmpp.uri.XMPPUri; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Locale; + /** * Main application activity. - * + * * @author alexander.ivanov - * */ -public class ContactList extends ManagedListActivity implements - OnContactChangedListener, OnAccountChangedListener, - OnChatChangedListener, View.OnClickListener, ConfirmDialogListener, - OnItemClickListener, OnLongClickListener { - - /** - * Select contact to be invited to the room was requested. - */ - private static final String ACTION_ROOM_INVITE = "com.xabber.android.ui.ContactList.ACTION_ROOM_INVITE"; - - private static final long CLOSE_ACTIVITY_AFTER_DELAY = 300; - - private static final String SAVED_ACTION = "com.xabber.android.ui.ContactList.SAVED_ACTION"; - private static final String SAVED_ACTION_WITH_ACCOUNT = "com.xabber.android.ui.ContactList.SAVED_ACTION_WITH_ACCOUNT"; - private static final String SAVED_ACTION_WITH_GROUP = "com.xabber.android.ui.ContactList.SAVED_ACTION_WITH_GROUP"; - private static final String SAVED_ACTION_WITH_USER = "com.xabber.android.ui.ContactList.SAVED_ACTION_WITH_USER"; - private static final String SAVED_SEND_TEXT = "com.xabber.android.ui.ContactList.SAVED_SEND_TEXT"; - private static final String SAVED_OPEN_DIALOG_USER = "com.xabber.android.ui.ContactList.SAVED_OPEN_DIALOG_USER"; - private static final String SAVED_OPEN_DIALOG_TEXT = "com.xabber.android.ui.ContactList.SAVED_OPEN_DIALOG_TEXT"; - - private static final int OPTION_MENU_ADD_CONTACT_ID = 0x02; - private static final int OPTION_MENU_STATUS_EDITOR_ID = 0x04; - private static final int OPTION_MENU_PREFERENCE_EDITOR_ID = 0x05; - private static final int OPTION_MENU_CHAT_LIST_ID = 0x06; - private static final int OPTION_MENU_JOIN_ROOM_ID = 0x07; - private static final int OPTION_MENU_EXIT_ID = 0x08; - private static final int OPTION_MENU_SEARCH_ID = 0x0A; - private static final int OPTION_MENU_CLOSE_CHATS_ID = 0x0B; - - private static final int CONTEXT_MENU_VIEW_CHAT_ID = 0x12; - private static final int CONTEXT_MENU_EDIT_CONTACT_ID = 0x13; - private static final int CONTEXT_MENU_DELETE_CONTACT_ID = 0x14; - private static final int CONTEXT_MENU_CLOSE_CHAT_ID = 0x15; - private static final int CONTEXT_MENU_REQUEST_SUBSCRIPTION_ID = 0x16; - private static final int CONTEXT_MENU_ACCEPT_SUBSCRIPTION_ID = 0x17; - private static final int CONTEXT_MENU_DISCARD_SUBSCRIPTION_ID = 0x18; - private static final int CONTEXT_MENU_LEAVE_ROOM_ID = 0x19; - private static final int CONTEXT_MENU_JOIN_ROOM_ID = 0x1A; - private static final int CONTEXT_MENU_EDIT_ROOM_ID = 0x1B; - private static final int CONTEXT_MENU_VIEW_CONTACT_ID = 0x1C; - - private static final int CONTEXT_MENU_GROUP_RENAME_ID = 0x31; - private static final int CONTEXT_MENU_GROUP_DELETE_ID = 0x32; - - private static final int CONTEXT_MENU_ACCOUNT_EDITOR_ID = 0x33; - private static final int CONTEXT_MENU_ACCOUNT_STATUS_ID = 0x34; - private static final int CONTEXT_MENU_ACCOUNT_ADD_CONTACT_ID = 0x35; - private static final int CONTEXT_MENU_ACCOUNT_RECONNECT_ID = 0x39; - private static final int CONTEXT_MENU_ACCOUNT_VCARD_ID = 0x3A; - - private static final int CONTEXT_MENU_SHOW_OFFLINE_GROUP_ID = 0x40; - private static final int CONTEXT_MENU_SHOW_OFFLINE_ALWAYS_ID = 0x41; - private static final int CONTEXT_MENU_SHOW_OFFLINE_NORMAL_ID = 0x42; - private static final int CONTEXT_MENU_SHOW_OFFLINE_NEVER_ID = 0x43; - - private static final int DIALOG_DELETE_CONTACT_ID = 0x50; - private static final int DIALOG_DELETE_GROUP_ID = 0x51; - private static final int DIALOG_RENAME_GROUP_ID = 0x52; - private static final int DIALOG_START_AT_BOOT_ID = 0x53; - private static final int DIALOG_CONTACT_INTEGRATION_ID = 0x54; - private static final int DIALOG_OPEN_WITH_ACCOUNT_ID = 0x55; - private static final int DIALOG_CLOSE_APPLICATION_ID = 0x57; - - /** - * Adapter for contact list. - */ - private ContactListAdapter contactListAdapter; - - /** - * Adapter for account list. - */ - private AccountToggleAdapter accountToggleAdapter; - - /** - * Current action. - */ - private String action; - - /** - * Dialog related values. - */ - private String actionWithAccount; - private String actionWithGroup; - private String actionWithUser; - private String sendText; - private String openDialogUser; - private String openDialogText; - - /** - * Title view. - */ - private View titleView; - - @Override - public void onCreate(Bundle savedInstanceState) { - if (Intent.ACTION_VIEW.equals(getIntent().getAction()) - || Intent.ACTION_SEND.equals(getIntent().getAction()) - || Intent.ACTION_SENDTO.equals(getIntent().getAction()) - || Intent.ACTION_CREATE_SHORTCUT - .equals(getIntent().getAction())) - ActivityManager.getInstance().startNewTask(this); - super.onCreate(savedInstanceState); - if (isFinishing()) - return; - - setContentView(R.layout.contact_list); - - titleView = findViewById(android.R.id.title); - - ListView listView = getListView(); - listView.setOnItemClickListener(this); - listView.setItemsCanFocus(true); - - registerForContextMenu(listView); - contactListAdapter = new ContactListAdapter(this); - setListAdapter(contactListAdapter); - accountToggleAdapter = new AccountToggleAdapter(this, this, - (LinearLayout) findViewById(R.id.account_list)); - - View commonStatusText = findViewById(R.id.common_status_text); - View commonStatusMode = findViewById(R.id.common_status_mode); - - TypedArray typedArray = obtainStyledAttributes(R.styleable.ContactList); - ColorStateList textColorPrimary = typedArray - .getColorStateList(R.styleable.ContactList_textColorPrimaryNoSelected); - Drawable titleMainBackground = typedArray - .getDrawable(R.styleable.ContactList_titleMainBackground); - typedArray.recycle(); - - ((TextView) commonStatusText).setTextColor(textColorPrimary); - titleView.setBackgroundDrawable(titleMainBackground); - - commonStatusText.setOnLongClickListener(this); - commonStatusMode.setOnClickListener(this); - commonStatusText.setOnClickListener(this); - titleView.setOnClickListener(this); - findViewById(R.id.button).setOnClickListener(this); - findViewById(R.id.back_button).setOnClickListener(this); - - if (savedInstanceState != null) { - actionWithAccount = savedInstanceState - .getString(SAVED_ACTION_WITH_ACCOUNT); - actionWithGroup = savedInstanceState - .getString(SAVED_ACTION_WITH_GROUP); - actionWithUser = savedInstanceState - .getString(SAVED_ACTION_WITH_USER); - sendText = savedInstanceState.getString(SAVED_SEND_TEXT); - openDialogUser = savedInstanceState - .getString(SAVED_OPEN_DIALOG_USER); - openDialogText = savedInstanceState - .getString(SAVED_OPEN_DIALOG_TEXT); - action = savedInstanceState.getString(SAVED_ACTION); - } else { - actionWithAccount = null; - actionWithGroup = null; - actionWithUser = null; - sendText = null; - openDialogUser = null; - openDialogText = null; - action = getIntent().getAction(); - } - getIntent().setAction(null); - } - - @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - setIntent(intent); - action = getIntent().getAction(); - getIntent().setAction(null); - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putString(SAVED_ACTION, action); - outState.putString(SAVED_ACTION_WITH_ACCOUNT, actionWithAccount); - outState.putString(SAVED_ACTION_WITH_GROUP, actionWithGroup); - outState.putString(SAVED_ACTION_WITH_USER, actionWithUser); - outState.putString(SAVED_SEND_TEXT, sendText); - outState.putString(SAVED_OPEN_DIALOG_USER, openDialogUser); - outState.putString(SAVED_OPEN_DIALOG_TEXT, openDialogText); - } - - /** - * Open chat with specified contact. - * - * Show dialog to choose account if necessary. - * - * @param user - * @param text - * can be null. - */ - private void openChat(String user, String text) { - String bareAddress = Jid.getBareAddress(user); - ArrayList entities = new ArrayList(); - for (AbstractChat check : MessageManager.getInstance().getChats()) - if (check.isActive() && check.getUser().equals(bareAddress)) - entities.add(check); - if (entities.size() == 1) { - openChat(entities.get(0), text); - return; - } - entities.clear(); - for (RosterContact check : RosterManager.getInstance().getContacts()) - if (check.isEnabled() && check.getUser().equals(bareAddress)) - entities.add(check); - if (entities.size() == 1) { - openChat(entities.get(0), text); - return; - } - Collection accounts = AccountManager.getInstance() - .getAccounts(); - if (accounts.isEmpty()) - return; - if (accounts.size() == 1) { - openChat(new BaseEntity(accounts.iterator().next(), bareAddress), - text); - return; - } - openDialogUser = bareAddress; - openDialogText = text; - showDialog(DIALOG_OPEN_WITH_ACCOUNT_ID); - } - - /** - * Open chat with specified contact and enter text to be sent. - * - * @param baseEntity - * @param text - * can be null. - */ - private void openChat(BaseEntity baseEntity, String text) { - if (text == null) - startActivity(ChatViewer.createSendIntent(this, - baseEntity.getAccount(), baseEntity.getUser(), null)); - else - startActivity(ChatViewer.createSendIntent(this, - baseEntity.getAccount(), baseEntity.getUser(), text)); - finish(); - } - - @Override - protected void onResume() { - super.onResume(); - updateStatusBar(); - rebuildAccountToggler(); - - Application.getInstance().addUIListener(OnAccountChangedListener.class, - this); - Application.getInstance().addUIListener(OnContactChangedListener.class, - this); - Application.getInstance().addUIListener(OnChatChangedListener.class, - this); - contactListAdapter.onChange(); - - if (ContactList.ACTION_ROOM_INVITE.equals(action) - || Intent.ACTION_SEND.equals(action) - || Intent.ACTION_CREATE_SHORTCUT.equals(action)) { - if (Intent.ACTION_SEND.equals(action)) - sendText = getIntent().getStringExtra(Intent.EXTRA_TEXT); - Toast.makeText(this, getString(R.string.select_contact), - Toast.LENGTH_LONG).show(); - } else if (Intent.ACTION_VIEW.equals(action)) { - action = null; - Uri data = getIntent().getData(); - if (data != null && "xmpp".equals(data.getScheme())) { - XMPPUri xmppUri; - try { - xmppUri = XMPPUri.parse(data); - } catch (IllegalArgumentException e) { - xmppUri = null; - } - if (xmppUri != null && "message".equals(xmppUri.getQueryType())) { - ArrayList texts = xmppUri.getValues("body"); - String text = null; - if (texts != null && !texts.isEmpty()) - text = texts.get(0); - openChat(xmppUri.getPath(), text); - } - } - } else if (Intent.ACTION_SENDTO.equals(action)) { - action = null; - Uri data = getIntent().getData(); - if (data != null) { - String path = data.getPath(); - if (path != null && path.startsWith("/")) - openChat(path.substring(1), null); - } - } - if (Application.getInstance().doNotify()) { - if (SettingsManager.bootCount() > 2 - && !SettingsManager.connectionStartAtBoot() - && !SettingsManager.startAtBootSuggested()) - showDialog(DIALOG_START_AT_BOOT_ID); - if (!SettingsManager.contactIntegrationSuggested() - && Application.getInstance().isContactsSupported()) { - if (AccountManager.getInstance().getAllAccounts().isEmpty()) - SettingsManager.setContactIntegrationSuggested(); - else - showDialog(DIALOG_CONTACT_INTEGRATION_ID); - } - } - } - - @Override - protected void onPause() { - super.onPause(); - unregisterListeners(); - } - - private void unregisterListeners() { - Application.getInstance().removeUIListener( - OnAccountChangedListener.class, this); - Application.getInstance().removeUIListener( - OnContactChangedListener.class, this); - Application.getInstance().removeUIListener(OnChatChangedListener.class, - this); - contactListAdapter.removeRefreshRequests(); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - menu.add(0, OPTION_MENU_ADD_CONTACT_ID, 0, - getText(R.string.contact_add)).setIcon( - R.drawable.ic_menu_invite); - menu.add(0, OPTION_MENU_CLOSE_CHATS_ID, 0, - getText(R.string.close_chats)).setIcon( - R.drawable.ic_menu_end_conversation); - menu.add(0, OPTION_MENU_PREFERENCE_EDITOR_ID, 0, - getResources().getText(R.string.preference_editor)).setIcon( - android.R.drawable.ic_menu_preferences); - menu.add(0, OPTION_MENU_STATUS_EDITOR_ID, 0, - getText(R.string.status_editor)).setIcon( - R.drawable.ic_menu_notifications); - menu.add(0, OPTION_MENU_EXIT_ID, 0, getText(R.string.exit)).setIcon( - android.R.drawable.ic_menu_close_clear_cancel); - menu.add(0, OPTION_MENU_JOIN_ROOM_ID, 0, getText(R.string.muc_add)); - menu.add(0, OPTION_MENU_SEARCH_ID, 0, - getText(android.R.string.search_go)); - menu.add(0, OPTION_MENU_CHAT_LIST_ID, 0, getText(R.string.chat_list)) - .setIcon(R.drawable.ic_menu_friendslist); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - super.onOptionsItemSelected(item); - switch (item.getItemId()) { - case OPTION_MENU_ADD_CONTACT_ID: - startActivity(ContactAdd.createIntent(this)); - return true; - case OPTION_MENU_STATUS_EDITOR_ID: - startActivity(StatusEditor.createIntent(this)); - return true; - case OPTION_MENU_PREFERENCE_EDITOR_ID: - startActivity(PreferenceEditor.createIntent(this)); - return true; - case OPTION_MENU_CHAT_LIST_ID: - startActivity(ChatList.createIntent(this)); - return true; - case OPTION_MENU_JOIN_ROOM_ID: - startActivity(MUCEditor.createIntent(this)); - return true; - case OPTION_MENU_EXIT_ID: - Application.getInstance().requestToClose(); - showDialog(DIALOG_CLOSE_APPLICATION_ID); - unregisterListeners(); - new Handler().postDelayed(new Runnable() { - @Override - public void run() { - // Close activity if application was not killed yet. - finish(); - } - }, CLOSE_ACTIVITY_AFTER_DELAY); - return true; - case OPTION_MENU_SEARCH_ID: - search(); - return true; - case OPTION_MENU_CLOSE_CHATS_ID: - for (AbstractChat chat : MessageManager.getInstance() - .getActiveChats()) { - MessageManager.getInstance().closeChat(chat.getAccount(), - chat.getUser()); - NotificationManager.getInstance().removeMessageNotification( - chat.getAccount(), chat.getUser()); - } - contactListAdapter.onChange(); - return true; - } - return false; - } - - @Override - public void onCreateContextMenu(ContextMenu menu, View view, - ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, view, menuInfo); - if (view == getListView()) { - final AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; - BaseEntity baseEntity = (BaseEntity) getListView() - .getItemAtPosition(info.position); - if (baseEntity == null) - // Account toggler - return; - if (baseEntity instanceof AbstractContact) { - // Contact - actionWithAccount = baseEntity.getAccount(); - actionWithGroup = null; - actionWithUser = baseEntity.getUser(); - AbstractContact abstractContact = (AbstractContact) baseEntity; - menu.setHeaderTitle(abstractContact.getName()); - menu.add(0, CONTEXT_MENU_VIEW_CHAT_ID, 0, getResources() - .getText(R.string.chat_viewer)); - if (MUCManager.getInstance().hasRoom(actionWithAccount, - actionWithUser)) { - if (!MUCManager.getInstance().inUse(actionWithAccount, - actionWithUser)) - menu.add(0, CONTEXT_MENU_EDIT_ROOM_ID, 0, - getResources().getText(R.string.muc_edit)); - menu.add(0, CONTEXT_MENU_DELETE_CONTACT_ID, 0, - getResources().getText(R.string.muc_delete)); - if (MUCManager.getInstance().isDisabled(actionWithAccount, - actionWithUser)) - menu.add(0, CONTEXT_MENU_JOIN_ROOM_ID, 0, - getResources().getText(R.string.muc_join)); - else - menu.add(0, CONTEXT_MENU_LEAVE_ROOM_ID, 0, - getResources().getText(R.string.muc_leave)); - } else { - menu.add(0, CONTEXT_MENU_VIEW_CONTACT_ID, 0, getResources() - .getText(R.string.contact_viewer)); - menu.add(0, CONTEXT_MENU_EDIT_CONTACT_ID, 0, getResources() - .getText(R.string.contact_editor)); - menu.add(0, CONTEXT_MENU_DELETE_CONTACT_ID, 0, - getResources().getText(R.string.contact_delete)); - if (MessageManager.getInstance().hasActiveChat( - actionWithAccount, actionWithUser)) - menu.add(0, CONTEXT_MENU_CLOSE_CHAT_ID, 0, - getResources().getText(R.string.close_chat)); - if (abstractContact.getStatusMode() == StatusMode.unsubscribed) - menu.add(0, CONTEXT_MENU_REQUEST_SUBSCRIPTION_ID, 0, - getText(R.string.request_subscription)); - } - if (PresenceManager.getInstance().hasSubscriptionRequest( - actionWithAccount, actionWithUser)) { - menu.add(0, CONTEXT_MENU_ACCEPT_SUBSCRIPTION_ID, 0, - getResources() - .getText(R.string.accept_subscription)); - menu.add(0, CONTEXT_MENU_DISCARD_SUBSCRIPTION_ID, 0, - getText(R.string.discard_subscription)); - } - return; - } else if (baseEntity instanceof GroupConfiguration) { - // Group or account in contact list - actionWithAccount = baseEntity.getAccount(); - actionWithGroup = baseEntity.getUser(); - actionWithUser = null; - - if (baseEntity instanceof AccountConfiguration) { - actionWithGroup = null; - } else { - // Group - menu.setHeaderTitle(GroupManager.getInstance() - .getGroupName(actionWithAccount, actionWithGroup)); - if (actionWithGroup != GroupManager.ACTIVE_CHATS - && actionWithGroup != GroupManager.IS_ROOM) { - menu.add(0, CONTEXT_MENU_GROUP_RENAME_ID, 0, - getText(R.string.group_rename)); - if (actionWithGroup != GroupManager.NO_GROUP) - menu.add(0, CONTEXT_MENU_GROUP_DELETE_ID, 0, - getText(R.string.group_remove)); - } - } - } else { - return; - } - } else { - // Account panel - actionWithAccount = (String) accountToggleAdapter - .getItemForView(view); - actionWithGroup = null; - actionWithUser = null; - } - // Group or account - - if (actionWithGroup == null) { - // Account - menu.setHeaderTitle(AccountManager.getInstance().getVerboseName( - actionWithAccount)); - AccountItem accountItem = AccountManager.getInstance().getAccount( - actionWithAccount); - ConnectionState state = accountItem.getState(); - if (state == ConnectionState.waiting) - menu.add(0, CONTEXT_MENU_ACCOUNT_RECONNECT_ID, 0, - getText(R.string.account_reconnect)); - menu.add(0, CONTEXT_MENU_ACCOUNT_STATUS_ID, 0, - getText(R.string.status_editor)); - menu.add(0, CONTEXT_MENU_ACCOUNT_EDITOR_ID, 0, - getText(R.string.account_editor)); - if (state.isConnected()) { - menu.add(0, CONTEXT_MENU_ACCOUNT_VCARD_ID, 0, - getText(R.string.contact_viewer)); - menu.add(0, CONTEXT_MENU_ACCOUNT_ADD_CONTACT_ID, 0, - getText(R.string.contact_add)); - } - } - - if (actionWithGroup != null || SettingsManager.contactsShowAccounts()) { - SubMenu mapMode = menu.addSubMenu(getResources().getText( - R.string.show_offline_settings)); - mapMode.setHeaderTitle(R.string.show_offline_settings); - MenuItem always = mapMode.add(CONTEXT_MENU_SHOW_OFFLINE_GROUP_ID, - CONTEXT_MENU_SHOW_OFFLINE_ALWAYS_ID, 0, getResources() - .getText(R.string.show_offline_always)); - MenuItem normal = mapMode.add(CONTEXT_MENU_SHOW_OFFLINE_GROUP_ID, - CONTEXT_MENU_SHOW_OFFLINE_NORMAL_ID, 0, getResources() - .getText(R.string.show_offline_normal)); - MenuItem never = mapMode.add(CONTEXT_MENU_SHOW_OFFLINE_GROUP_ID, - CONTEXT_MENU_SHOW_OFFLINE_NEVER_ID, 0, getResources() - .getText(R.string.show_offline_never)); - mapMode.setGroupCheckable(CONTEXT_MENU_SHOW_OFFLINE_GROUP_ID, true, - true); - ShowOfflineMode showOfflineMode = GroupManager.getInstance() - .getShowOfflineMode( - actionWithAccount, - actionWithGroup == null ? GroupManager.IS_ACCOUNT - : actionWithGroup); - if (showOfflineMode == ShowOfflineMode.always) - always.setChecked(true); - else if (showOfflineMode == ShowOfflineMode.normal) - normal.setChecked(true); - else if (showOfflineMode == ShowOfflineMode.never) - never.setChecked(true); - else - throw new IllegalStateException(); - } - } - - @Override - public boolean onContextItemSelected(MenuItem item) { - super.onContextItemSelected(item); - switch (item.getItemId()) { - // Contact - case CONTEXT_MENU_VIEW_CHAT_ID: - MessageManager.getInstance().openChat(actionWithAccount, - actionWithUser); - startActivity(ChatViewer.createIntent(this, actionWithAccount, - actionWithUser)); - return true; - case CONTEXT_MENU_VIEW_CONTACT_ID: - startActivity(ContactViewer.createIntent(this, actionWithAccount, - actionWithUser)); - return true; - case CONTEXT_MENU_EDIT_CONTACT_ID: - startActivity(ContactEditor.createIntent(this, actionWithAccount, - actionWithUser)); - return true; - case CONTEXT_MENU_DELETE_CONTACT_ID: - showDialog(DIALOG_DELETE_CONTACT_ID); - return true; - case CONTEXT_MENU_EDIT_ROOM_ID: - startActivity(MUCEditor.createIntent(this, actionWithAccount, - actionWithUser)); - return true; - case CONTEXT_MENU_JOIN_ROOM_ID: - MUCManager.getInstance().joinRoom(actionWithAccount, - actionWithUser, true); - return true; - case CONTEXT_MENU_LEAVE_ROOM_ID: - MUCManager.getInstance().leaveRoom(actionWithAccount, - actionWithUser); - MessageManager.getInstance().closeChat(actionWithAccount, - actionWithUser); - NotificationManager.getInstance().removeMessageNotification( - actionWithAccount, actionWithUser); - contactListAdapter.onChange(); - return true; - case CONTEXT_MENU_CLOSE_CHAT_ID: - MessageManager.getInstance().closeChat(actionWithAccount, - actionWithUser); - NotificationManager.getInstance().removeMessageNotification( - actionWithAccount, actionWithUser); - contactListAdapter.onChange(); - return true; - case CONTEXT_MENU_REQUEST_SUBSCRIPTION_ID: - try { - PresenceManager.getInstance().requestSubscription( - actionWithAccount, actionWithUser); - } catch (NetworkException e) { - Application.getInstance().onError(e); - } - return true; - case CONTEXT_MENU_ACCEPT_SUBSCRIPTION_ID: - try { - PresenceManager.getInstance().acceptSubscription( - actionWithAccount, actionWithUser); - } catch (NetworkException e) { - Application.getInstance().onError(e); - } - startActivity(ContactEditor.createIntent(this, actionWithAccount, - actionWithUser)); - return true; - case CONTEXT_MENU_DISCARD_SUBSCRIPTION_ID: - try { - PresenceManager.getInstance().discardSubscription( - actionWithAccount, actionWithUser); - } catch (NetworkException e) { - Application.getInstance().onError(e); - } - return true; - - // Group - case CONTEXT_MENU_GROUP_RENAME_ID: - showDialog(DIALOG_RENAME_GROUP_ID); - return true; - case CONTEXT_MENU_GROUP_DELETE_ID: - showDialog(DIALOG_DELETE_GROUP_ID); - return true; - - // Account - case CONTEXT_MENU_ACCOUNT_RECONNECT_ID: - if (AccountManager.getInstance().getAccount(actionWithAccount) - .updateConnection(true)) - AccountManager.getInstance() - .onAccountChanged(actionWithAccount); - return true; - case CONTEXT_MENU_ACCOUNT_VCARD_ID: - String user = AccountManager.getInstance() - .getAccount(actionWithAccount).getRealJid(); - if (user == null) - Application.getInstance().onError(R.string.NOT_CONNECTED); - else { - startActivity(ContactViewer.createIntent(this, - actionWithAccount, user)); - } - return true; - case CONTEXT_MENU_ACCOUNT_EDITOR_ID: - startActivity(AccountEditor.createIntent(this, actionWithAccount)); - return true; - case CONTEXT_MENU_ACCOUNT_STATUS_ID: - startActivity(StatusEditor.createIntent(this, actionWithAccount)); - return true; - case CONTEXT_MENU_ACCOUNT_ADD_CONTACT_ID: - startActivity(ContactAdd.createIntent(this, actionWithAccount)); - return true; - - // Groups or account - case CONTEXT_MENU_SHOW_OFFLINE_ALWAYS_ID: - GroupManager.getInstance().setShowOfflineMode( - actionWithAccount, - actionWithGroup == null ? GroupManager.IS_ACCOUNT - : actionWithGroup, ShowOfflineMode.always); - contactListAdapter.onChange(); - return true; - case CONTEXT_MENU_SHOW_OFFLINE_NORMAL_ID: - GroupManager.getInstance().setShowOfflineMode( - actionWithAccount, - actionWithGroup == null ? GroupManager.IS_ACCOUNT - : actionWithGroup, ShowOfflineMode.normal); - contactListAdapter.onChange(); - return true; - case CONTEXT_MENU_SHOW_OFFLINE_NEVER_ID: - GroupManager.getInstance().setShowOfflineMode( - actionWithAccount, - actionWithGroup == null ? GroupManager.IS_ACCOUNT - : actionWithGroup, ShowOfflineMode.never); - contactListAdapter.onChange(); - return true; - } - return false; - } - - @Override - protected Dialog onCreateDialog(int id) { - super.onCreateDialog(id); - switch (id) { - case DIALOG_DELETE_CONTACT_ID: - int resource; - if (MUCManager.getInstance().hasRoom(actionWithAccount, - actionWithUser)) - resource = R.string.muc_delete_confirm; - else - resource = R.string.contact_delete_confirm; - return new ConfirmDialogBuilder(this, DIALOG_DELETE_CONTACT_ID, - this).setMessage( - getString( - resource, - RosterManager.getInstance().getName( - actionWithAccount, actionWithUser), - AccountManager.getInstance().getVerboseName( - actionWithAccount))).create(); - case DIALOG_DELETE_GROUP_ID: - return new ConfirmDialogBuilder(this, DIALOG_DELETE_GROUP_ID, this) - .setMessage( - getString(R.string.group_remove_confirm, - actionWithGroup)).create(); - case DIALOG_RENAME_GROUP_ID: - return new GroupRenameDialogBuilder(this, DIALOG_RENAME_GROUP_ID, - this, actionWithGroup == GroupManager.NO_GROUP ? "" - : actionWithGroup).create(); - case DIALOG_START_AT_BOOT_ID: - return new ConfirmDialogBuilder(this, DIALOG_START_AT_BOOT_ID, this) - .setMessage(getString(R.string.start_at_boot_suggest)) - .create(); - case DIALOG_CONTACT_INTEGRATION_ID: - return new ConfirmDialogBuilder(this, - DIALOG_CONTACT_INTEGRATION_ID, this).setMessage( - getString(R.string.contact_integration_suggest)).create(); - case DIALOG_OPEN_WITH_ACCOUNT_ID: - return new AccountChooseDialogBuilder(this, - DIALOG_OPEN_WITH_ACCOUNT_ID, this, openDialogUser).create(); - case DIALOG_CLOSE_APPLICATION_ID: - ProgressDialog progressDialog = new ProgressDialog(this); - progressDialog - .setMessage(getString(R.string.application_state_closing)); - progressDialog.setOnCancelListener(new OnCancelListener() { - @Override - public void onCancel(DialogInterface dialog) { - finish(); - } - }); - progressDialog.setIndeterminate(true); - return progressDialog; - default: - return null; - } - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - switch (keyCode) { - case KeyEvent.KEYCODE_SEARCH: - search(); - return true; - } - return super.onKeyDown(keyCode, event); - } - - @Override - public void onClick(View view) { - switch (view.getId()) { - case R.id.common_status_mode: - startActivity(StatusEditor.createIntent(this)); - break; - case R.id.button: // Hint button - switch ((Integer) view.getTag()) { - case R.string.application_action_no_online: - SettingsManager.setContactsShowOffline(true); - contactListAdapter.onChange(); - break; - case R.string.application_action_no_contacts: - startActivity(ContactAdd.createIntent(this)); - break; - case R.string.application_action_waiting: - ConnectionManager.getInstance().updateConnections(true); - break; - case R.string.application_action_offline: - AccountManager.getInstance().setStatus(StatusMode.available, - null); - break; - case R.string.application_action_disabled: - startActivity(AccountList.createIntent(this)); - break; - case R.string.application_action_empty: - startActivity(AccountAdd.createIntent(this)); - break; - default: - break; - } - updateStatusBar(); - break; - case R.id.back_button: // Xabber icon button - case R.id.common_status_text: - case android.R.id.title: - scrollUp(); - break; - default: - String account = (String) accountToggleAdapter.getItemForView(view); - if (account == null) // Check for tap on account in the title - break; - ListView listView = getListView(); - if (!SettingsManager.contactsShowAccounts()) { - if (AccountManager.getInstance().getAccounts().size() < 2) { - scrollUp(); - } else { - if (account.equals(AccountManager.getInstance() - .getSelectedAccount())) - SettingsManager.setContactsSelectedAccount(""); - else - SettingsManager.setContactsSelectedAccount(account); - rebuildAccountToggler(); - contactListAdapter.onChange(); - stopMovement(); - } - } else { - long count = listView.getCount(); - for (int position = 0; position < (int) count; position++) { - BaseEntity baseEntity = (BaseEntity) listView - .getItemAtPosition(position); - if (baseEntity != null - && baseEntity instanceof AccountConfiguration - && baseEntity.getAccount().equals(account)) { - listView.setSelection(position); - stopMovement(); - break; - } - } - } - break; - } - } - - /** - * Stop fling scrolling. - */ - private void stopMovement() { - getListView().onTouchEvent( - MotionEvent.obtain(SystemClock.uptimeMillis(), - SystemClock.uptimeMillis(), MotionEvent.ACTION_CANCEL, - 0, 0, 0)); - } - - /** - * Scroll to the top of contact list. - */ - private void scrollUp() { - ListView listView = getListView(); - if (listView.getCount() > 0) - listView.setSelection(0); - stopMovement(); - } - - @Override - public boolean onLongClick(View view) { - switch (view.getId()) { - case R.id.common_status_text: - startActivity(StatusEditor.createIntent(this)); - return true; - } - return false; - } - - @Override - public void onItemClick(AdapterView parent, View view, int position, - long id) { - Object object = parent.getAdapter().getItem(position); - if (object == null) { - // Account toggler - } else if (object instanceof AbstractContact) { - AbstractContact abstractContact = (AbstractContact) object; - if (ACTION_ROOM_INVITE.equals(action)) { - action = null; - Intent intent = getIntent(); - String account = getRoomInviteAccount(intent); - String user = getRoomInviteUser(intent); - if (account != null && user != null) - try { - MUCManager.getInstance().invite(account, user, - abstractContact.getUser()); - } catch (NetworkException e) { - Application.getInstance().onError(e); - } - finish(); - } else if (Intent.ACTION_SEND.equals(action)) { - action = null; - startActivity(ChatViewer.createSendIntent(this, - abstractContact.getAccount(), - abstractContact.getUser(), sendText)); - finish(); - } else if (Intent.ACTION_CREATE_SHORTCUT.equals(action)) { - Intent intent = new Intent(); - intent.putExtra( - Intent.EXTRA_SHORTCUT_INTENT, - ChatViewer.createClearTopIntent(this, - abstractContact.getAccount(), - abstractContact.getUser())); - intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, - abstractContact.getName()); - Bitmap bitmap; - if (MUCManager.getInstance() - .hasRoom(abstractContact.getAccount(), - abstractContact.getUser())) - bitmap = AvatarManager.getInstance().getRoomBitmap( - abstractContact.getUser()); - else - bitmap = AvatarManager.getInstance().getUserBitmap( - abstractContact.getUser()); - intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, AvatarManager - .getInstance().createShortcutBitmap(bitmap)); - setResult(RESULT_OK, intent); - finish(); - } else { - startActivity(ChatViewer - .createIntent(this, abstractContact.getAccount(), - abstractContact.getUser())); - } - } else if (object instanceof GroupConfiguration) { - GroupConfiguration groupConfiguration = (GroupConfiguration) object; - contactListAdapter.setExpanded(groupConfiguration.getAccount(), - groupConfiguration.getUser(), - !groupConfiguration.isExpanded()); - } - } - - @Override - public void onContactsChanged(Collection addresses) { - contactListAdapter.refreshRequest(); - } - - @Override - public void onAccountsChanged(Collection accounts) { - accountToggleAdapter.onChange(); - contactListAdapter.refreshRequest(); - } - - @Override - public void onChatChanged(String account, String user, boolean incoming) { - if (incoming) - contactListAdapter.refreshRequest(); - } - - @Override - public void onAccept(DialogBuilder dialogBuilder) { - switch (dialogBuilder.getDialogId()) { - case DIALOG_DELETE_CONTACT_ID: - if (MUCManager.getInstance().hasRoom(actionWithAccount, - actionWithUser)) { - MUCManager.getInstance().removeRoom(actionWithAccount, - actionWithUser); - MessageManager.getInstance().closeChat(actionWithAccount, - actionWithUser); - NotificationManager.getInstance().removeMessageNotification( - actionWithAccount, actionWithUser); - } else - try { - RosterManager.getInstance().removeContact( - actionWithAccount, actionWithUser); - } catch (NetworkException e) { - Application.getInstance().onError(e); - } - break; - case DIALOG_DELETE_GROUP_ID: - try { - if (actionWithAccount == GroupManager.NO_ACCOUNT) - RosterManager.getInstance().removeGroup(actionWithGroup); - else - RosterManager.getInstance().removeGroup(actionWithAccount, - actionWithGroup); - } catch (NetworkException e) { - Application.getInstance().onError(e); - } - break; - case DIALOG_RENAME_GROUP_ID: - String name = ((GroupRenameDialogBuilder) dialogBuilder).getName(); - String source = actionWithGroup == GroupManager.NO_GROUP ? null - : actionWithGroup; - try { - if (actionWithAccount == GroupManager.NO_ACCOUNT) - RosterManager.getInstance().renameGroup(source, name); - else - RosterManager.getInstance().renameGroup(actionWithAccount, - source, name); - } catch (NetworkException e) { - Application.getInstance().onError(e); - } - break; - case DIALOG_START_AT_BOOT_ID: - SettingsManager.setStartAtBootSuggested(); - SettingsManager.setConnectionStartAtBoot(true); - break; - case DIALOG_CONTACT_INTEGRATION_ID: - SettingsManager.setContactIntegrationSuggested(); - for (String account : AccountManager.getInstance().getAllAccounts()) - AccountManager.getInstance().setSyncable(account, true); - break; - case DIALOG_OPEN_WITH_ACCOUNT_ID: - BaseEntity baseEntity = new BaseEntity( - ((AccountChooseDialogBuilder) dialogBuilder).getSelected(), - openDialogUser); - openChat(baseEntity, openDialogText); - break; - } - } - - @Override - public void onDecline(DialogBuilder dialogBuilder) { - switch (dialogBuilder.getDialogId()) { - case DIALOG_START_AT_BOOT_ID: - SettingsManager.setStartAtBootSuggested(); - break; - case DIALOG_CONTACT_INTEGRATION_ID: - SettingsManager.setContactIntegrationSuggested(); - break; - } - } - - @Override - public void onCancel(DialogBuilder dialogBuilder) { - } - - private void updateStatusBar() { - String statusText = SettingsManager.statusText(); - StatusMode statusMode = SettingsManager.statusMode(); - if ("".equals(statusText)) - statusText = getString(statusMode.getStringID()); - ((TextView) findViewById(R.id.common_status_text)).setText(statusText); - ((ImageView) findViewById(R.id.common_status_mode)) - .setImageLevel(statusMode.getStatusLevel()); - } - - private void rebuildAccountToggler() { - updateStatusBar(); - accountToggleAdapter.rebuild(); - if (SettingsManager.contactsShowPanel() - && accountToggleAdapter.getCount() > 0) - titleView.setVisibility(View.VISIBLE); - else - titleView.setVisibility(View.GONE); - } - - /** - * Show search dialog. - */ - private void search() { - InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - if (inputMethodManager != null) - inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, - 0); - } - - public static Intent createPersistentIntent(Context context) { - Intent intent = new Intent(context, ContactList.class); - intent.setAction("android.intent.action.MAIN"); - intent.addCategory("android.intent.category.LAUNCHER"); - intent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED - | Intent.FLAG_ACTIVITY_NEW_TASK); - return intent; - } - - public static Intent createIntent(Context context) { - return new Intent(context, ContactList.class); - } - - public static Intent createRoomInviteIntent(Context context, - String account, String room) { - Intent intent = new EntityIntentBuilder(context, ContactList.class) - .setAccount(account).setUser(room).build(); - intent.setAction(ACTION_ROOM_INVITE); - return intent; - } - - private static String getRoomInviteAccount(Intent intent) { - return EntityIntentBuilder.getAccount(intent); - } - - private static String getRoomInviteUser(Intent intent) { - return EntityIntentBuilder.getUser(intent); - } - +public class ContactList extends ManagedActivity implements OnAccountChangedListener, + View.OnClickListener, OnChoosedListener, ContactListFragmentListener, ContactListDrawerFragment.ContactListDrawerListener, Toolbar.OnMenuItemClickListener { + + /** + * Select contact to be invited to the room was requested. + */ + private static final String ACTION_ROOM_INVITE = "com.xabber.android.ui.ContactList.ACTION_ROOM_INVITE"; + + private static final long CLOSE_ACTIVITY_AFTER_DELAY = 300; + + private static final String SAVED_ACTION = "com.xabber.android.ui.ContactList.SAVED_ACTION"; + private static final String SAVED_SEND_TEXT = "com.xabber.android.ui.ContactList.SAVED_SEND_TEXT"; + + private static final int DIALOG_CLOSE_APPLICATION_ID = 0x57; + + private static final String CONTACT_LIST_TAG = "CONTACT_LIST"; + + /** + * Current action. + */ + private String action; + + /** + * Dialog related values. + */ + private String sendText; + + private SearchView searchView; + private BarPainter barPainter; + private ActionBarDrawerToggle drawerToggle; + private DrawerLayout drawerLayout; + private Menu optionsMenu; + + public static Intent createPersistentIntent(Context context) { + Intent intent = new Intent(context, ContactList.class); + intent.setAction("android.intent.action.MAIN"); + intent.addCategory("android.intent.category.LAUNCHER"); + intent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | Intent.FLAG_ACTIVITY_NEW_TASK); + return intent; + } + + public static Intent createIntent(Context context) { + return new Intent(context, ContactList.class); + } + + public static Intent createRoomInviteIntent(Context context, String account, String room) { + Intent intent = new EntityIntentBuilder(context, ContactList.class) + .setAccount(account).setUser(room).build(); + intent.setAction(ACTION_ROOM_INVITE); + return intent; + } + + private static String getRoomInviteAccount(Intent intent) { + return EntityIntentBuilder.getAccount(intent); + } + + private static String getRoomInviteUser(Intent intent) { + return EntityIntentBuilder.getUser(intent); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + if (Intent.ACTION_VIEW.equals(getIntent().getAction()) + || Intent.ACTION_SEND.equals(getIntent().getAction()) + || Intent.ACTION_SENDTO.equals(getIntent().getAction()) + || Intent.ACTION_CREATE_SHORTCUT.equals(getIntent().getAction())) { + ActivityManager.getInstance().startNewTask(this); + } + super.onCreate(savedInstanceState); + + if (isFinishing()) { + return; + } + + setContentView(R.layout.contact_list); + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_default); + toolbar.setOnClickListener(this); + + drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); + drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.application_title_short, R.string.application_title_short); + drawerLayout.setDrawerListener(drawerToggle); + + toolbar.inflateMenu(R.menu.contact_list); + optionsMenu = toolbar.getMenu(); + setUpSearchView(optionsMenu); + toolbar.setOnMenuItemClickListener(this); + + barPainter = new BarPainter(this, toolbar); + barPainter.setDefaultColor(); + + toolbar.setTitle(R.string.application_title_full); + + if (savedInstanceState != null) { + sendText = savedInstanceState.getString(SAVED_SEND_TEXT); + action = savedInstanceState.getString(SAVED_ACTION); + } else { + getSupportFragmentManager().beginTransaction().add(R.id.container, + new ContactListFragment(), CONTACT_LIST_TAG).commit(); + + sendText = null; + action = getIntent().getAction(); + } + getIntent().setAction(null); + } + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + setIntent(intent); + action = getIntent().getAction(); + getIntent().setAction(null); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putString(SAVED_ACTION, action); + outState.putString(SAVED_SEND_TEXT, sendText); + } + + /** + * Open chat with specified contact. + *

+ * Show dialog to choose account if necessary. + * + * @param user + * @param text can be null. + */ + private void openChat(String user, String text) { + String bareAddress = Jid.getBareAddress(user); + ArrayList entities = new ArrayList<>(); + for (AbstractChat check : MessageManager.getInstance().getChats()) { + if (check.isActive() && check.getUser().equals(bareAddress)) { + entities.add(check); + } + } + if (entities.size() == 1) { + openChat(entities.get(0), text); + return; + } + entities.clear(); + for (RosterContact check : RosterManager.getInstance().getContacts()) { + if (check.isEnabled() && check.getUser().equals(bareAddress)) { + entities.add(check); + } + } + if (entities.size() == 1) { + openChat(entities.get(0), text); + return; + } + Collection accounts = AccountManager.getInstance() + .getAccounts(); + if (accounts.isEmpty()) { + return; + } + if (accounts.size() == 1) { + openChat(new BaseEntity(accounts.iterator().next(), bareAddress), text); + return; + } + AccountChooseDialogFragment.newInstance(bareAddress, text) + .show(getFragmentManager(), "OPEN_WITH_ACCOUNT"); + } + + /** + * Open chat with specified contact and enter text to be sent. + * + * @param baseEntity + * @param text can be null. + */ + private void openChat(BaseEntity baseEntity, String text) { + if (text == null) { + startActivity(ChatViewer.createSendIntent(this, + baseEntity.getAccount(), baseEntity.getUser(), null)); + } else { + startActivity(ChatViewer.createSendIntent(this, + baseEntity.getAccount(), baseEntity.getUser(), text)); + } + finish(); + } + + @Override + protected void onResume() { + super.onResume(); + barPainter.setDefaultColor(); + rebuildAccountToggle(); + Application.getInstance().addUIListener(OnAccountChangedListener.class, this); + + if (action != null) { + switch (action) { + case ContactList.ACTION_ROOM_INVITE: + case Intent.ACTION_SEND: + case Intent.ACTION_CREATE_SHORTCUT: + if (Intent.ACTION_SEND.equals(action)) { + sendText = getIntent().getStringExtra(Intent.EXTRA_TEXT); + } + Toast.makeText(this, getString(R.string.select_contact), Toast.LENGTH_LONG).show(); + break; + case Intent.ACTION_VIEW: { + action = null; + Uri data = getIntent().getData(); + if (data != null && "xmpp".equals(data.getScheme())) { + XMPPUri xmppUri; + try { + xmppUri = XMPPUri.parse(data); + } catch (IllegalArgumentException e) { + xmppUri = null; + } + if (xmppUri != null && "message".equals(xmppUri.getQueryType())) { + ArrayList texts = xmppUri.getValues("body"); + String text = null; + if (texts != null && !texts.isEmpty()) { + text = texts.get(0); + } + openChat(xmppUri.getPath(), text); + } + } + break; + } + case Intent.ACTION_SENDTO: { + action = null; + Uri data = getIntent().getData(); + if (data != null) { + String path = data.getPath(); + if (path != null && path.startsWith("/")) { + openChat(path.substring(1), null); + } + } + break; + } + } + } + + if (Application.getInstance().doNotify()) { + if (!SettingsManager.isTranslationSuggested()) { + Locale currentLocale = getResources().getConfiguration().locale; + if (!currentLocale.getLanguage().equals("en") && !getResources().getBoolean(R.bool.is_translated)) { + new TranslationDialog().show(getFragmentManager(), "TRANSLATION_DIALOG"); + SettingsManager.setTranslationSuggested(); + } + } + + if (SettingsManager.bootCount() > 2 && !SettingsManager.connectionStartAtBoot() + && !SettingsManager.startAtBootSuggested()) { + StartAtBootDialogFragment.newInstance().show(getFragmentManager(), "START_AT_BOOT"); + } + if (!SettingsManager.contactIntegrationSuggested() + && Application.getInstance().isContactsSupported()) { + if (AccountManager.getInstance().getAllAccounts().isEmpty()) { + SettingsManager.setContactIntegrationSuggested(); + } else { + ContactIntegrationDialogFragment.newInstance() + .show(getFragmentManager(), "CONTACT_INTEGRATION"); + } + } + } + } + + @Override + protected void onPause() { + super.onPause(); + hideKeyboard(); + Application.getInstance().removeUIListener(OnAccountChangedListener.class, this); + } + + private void hideKeyboard() { + if (getCurrentFocus() != null) { + InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE); + inputMethodManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + getMenuInflater().inflate(R.menu.contact_list, menu); + menu.findItem(R.id.action_search).setVisible(false); + return true; + } + + private void setUpSearchView(final Menu menu) { + searchView = (SearchView) menu.findItem(R.id.action_search).getActionView(); + searchView.setQueryHint(getString(R.string.contact_search_hint)); + + searchView.setOnCloseListener(new SearchView.OnCloseListener() { + @Override + public boolean onClose() { + menu.findItem(R.id.action_search).collapseActionView(); + return true; + } + }); + + MenuItemCompat.setOnActionExpandListener(menu.findItem(R.id.action_search), new MenuItemCompat.OnActionExpandListener() { + @Override + public boolean onMenuItemActionExpand(MenuItem item) { + searchView.requestFocus(); + ((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)). + toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY); + return true; + } + + @Override + public boolean onMenuItemActionCollapse(MenuItem item) { + searchView.setQuery("", true); + searchView.clearFocus(); + return true; + } + }); + + searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { + @Override + public boolean onQueryTextSubmit(String query) { + return false; + } + + @Override + public boolean onQueryTextChange(String newText) { + Fragment fragmentById = getSupportFragmentManager().findFragmentById(R.id.container); + ((ContactListFragment) fragmentById).getFilterableAdapter().getFilter().filter(newText); + return true; + } + }); + } + + @Override + public boolean onMenuItemClick(MenuItem item) { + return onOptionsItemSelected(item); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (drawerToggle.onOptionsItemSelected(item)) { + return true; + } + + switch (item.getItemId()) { + case R.id.action_search: + searchView.setIconified(false); + return true; + case R.id.action_change_status: + startActivity(StatusEditor.createIntent(this)); + return true; + case R.id.action_add_contact: + startActivity(ContactAdd.createIntent(this)); + return true; + case R.id.action_close_chats: + closeAllChats(); + return true; + case R.id.action_join_conference: + startActivity(ConferenceAdd.createIntent(this)); + return true; + case R.id.action_chat_list: + startActivity(ChatViewer.createRecentChatsIntent(this)); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + @Override + protected void onPostCreate(Bundle savedInstanceState) { + super.onPostCreate(savedInstanceState); + drawerToggle.syncState(); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + drawerToggle.onConfigurationChanged(newConfig); + } + + @Override + public void onBackPressed() { + if (drawerLayout.isDrawerOpen(Gravity.START | Gravity.LEFT)) { + drawerLayout.closeDrawers(); + return; + } + super.onBackPressed(); + } + + private void exit() { + Application.getInstance().requestToClose(); + showDialog(DIALOG_CLOSE_APPLICATION_ID); + getContactListFragment().unregisterListeners(); + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + // Close activity if application was not killed yet. + finish(); + } + }, CLOSE_ACTIVITY_AFTER_DELAY); + } + + private void closeAllChats() { + for (AbstractChat chat : MessageManager.getInstance().getActiveChats()) { + MessageManager.getInstance().closeChat(chat.getAccount(), chat.getUser()); + NotificationManager.getInstance(). + removeMessageNotification(chat.getAccount(), chat.getUser()); + } + getContactListFragment().getAdapter().onChange(); + } + + private ContactListFragment getContactListFragment() { + return (ContactListFragment) getSupportFragmentManager().findFragmentByTag(CONTACT_LIST_TAG); + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, view, menuInfo); + } + + @Override + protected Dialog onCreateDialog(int id) { + super.onCreateDialog(id); + switch (id) { + case DIALOG_CLOSE_APPLICATION_ID: + ProgressDialog progressDialog = new ProgressDialog(this); + progressDialog.setMessage(getString(R.string.application_state_closing)); + progressDialog.setOnCancelListener(new OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + finish(); + } + }); + progressDialog.setIndeterminate(true); + return progressDialog; + default: + return null; + } + } + + @Override + public void onClick(View view) { + switch (view.getId()) { + case R.id.toolbar_default: + getContactListFragment().scrollUp(); + break; + } + } + + @Override + public void onContactClick(AbstractContact abstractContact) { + if (action == null) { + startActivity(ChatViewer.createSpecificChatIntent(this, + abstractContact.getAccount(), abstractContact.getUser())); + return; + } + switch (action) { + case ACTION_ROOM_INVITE: { + action = null; + Intent intent = getIntent(); + String account = getRoomInviteAccount(intent); + String user = getRoomInviteUser(intent); + if (account != null && user != null) { + try { + MUCManager.getInstance().invite(account, user, abstractContact.getUser()); + } catch (NetworkException e) { + Application.getInstance().onError(e); + } + } + finish(); + break; + } + case Intent.ACTION_SEND: + action = null; + startActivity(ChatViewer.createSendIntent(this, + abstractContact.getAccount(), abstractContact.getUser(), sendText)); + finish(); + break; + case Intent.ACTION_CREATE_SHORTCUT: { + createShortcut(abstractContact); + finish(); + break; + } + default: + startActivity(ChatViewer.createSpecificChatIntent(this, + abstractContact.getAccount(), abstractContact.getUser())); + break; + } + } + + @Override + public void onContactListChange(CommonState commonState) { + + switch (commonState) { + + case empty: + case disabled: + for (int i = 0; i < optionsMenu.size(); i++) { + optionsMenu.getItem(i).setVisible(false); + } + break; + case offline: + case waiting: + case connecting: + case roster: + case online: + for (int i = 0; i < optionsMenu.size(); i++) { + optionsMenu.getItem(i).setVisible(true); + } + + break; + } + } + + private void createShortcut(AbstractContact abstractContact) { + Intent intent = new Intent(); + intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, ChatViewer.createShortCutIntent(this, + abstractContact.getAccount(), abstractContact.getUser())); + intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, abstractContact.getName()); + Bitmap bitmap; + if (MUCManager.getInstance().hasRoom(abstractContact.getAccount(), + abstractContact.getUser())) { + bitmap = AvatarManager.getInstance().getRoomBitmap(abstractContact.getUser()); + } else { + bitmap = AvatarManager.getInstance().getUserBitmap(abstractContact.getUser()); + } + intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, + AvatarManager.getInstance().createShortcutBitmap(bitmap)); + setResult(RESULT_OK, intent); + } + + @Override + public void onAccountsChanged(Collection accounts) { + ((ContactListFragment)getSupportFragmentManager().findFragmentById(R.id.container)).onAccountsChanged(); + barPainter.setDefaultColor(); + } + + @Override + public void onChoose(String account, String user, String text) { + openChat(new BaseEntity(account, user), text); + } + + private void rebuildAccountToggle() { + ((ContactListFragment)getSupportFragmentManager().findFragmentById(R.id.container)).rebuild(); + } + + @Override + public void onContactListDrawerListener(int viewId) { + drawerLayout.closeDrawers(); + switch (viewId) { + case R.id.drawer_action_settings: + startActivity(PreferenceEditor.createIntent(this)); + break; + case R.id.drawer_action_about: + startActivity(AboutViewer.createIntent(this)); + break; + case R.id.drawer_action_exit: + exit(); + break; + case R.id.drawer_header_action_xmpp_accounts: + startActivity(AccountList.createIntent(this)); + break; + } + } + + @Override + public void onAccountSelected(String account) { + drawerLayout.closeDrawers(); + startActivity(AccountEditor.createIntent(this, account)); + } } diff --git a/app/src/main/java/com/xabber/android/ui/ContactListDrawerFragment.java b/app/src/main/java/com/xabber/android/ui/ContactListDrawerFragment.java new file mode 100644 index 0000000000..644df514e7 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/ContactListDrawerFragment.java @@ -0,0 +1,138 @@ +package com.xabber.android.ui; + + +import android.app.Activity; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.TextView; + +import com.xabber.android.R; +import com.xabber.android.data.Application; +import com.xabber.android.data.account.OnAccountChangedListener; +import com.xabber.android.ui.adapter.NavigationDrawerAccountAdapter; +import com.xabber.android.ui.helper.AccountPainter; + +import java.util.Collection; + +public class ContactListDrawerFragment extends Fragment implements View.OnClickListener, OnAccountChangedListener, AdapterView.OnItemClickListener { + + ContactListDrawerListener listener; + private NavigationDrawerAccountAdapter adapter; + private ListView listView; + private View divider; + private View headerTitle; + private ImageView drawerHeaderImage; + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + listener = (ContactListDrawerListener) activity; + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.contact_list_drawer, container, false); + + try { + ((TextView)view.findViewById(R.id.version)) + .setText(getActivity().getPackageManager().getPackageInfo(getActivity().getPackageName(), 0) + .versionName); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + + View drawerHeader = view.findViewById(R.id.drawer_header); + drawerHeaderImage = (ImageView) drawerHeader.findViewById(R.id.drawer_header_image); + + listView = (ListView) view.findViewById(R.id.drawer_account_list); + + View footerView = ((LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE)) + .inflate(R.layout.contact_list_drawer_footer, listView, false); + listView.addFooterView(footerView); + + View headerView = ((LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE)) + .inflate(R.layout.contact_list_drawer_header, listView, false); + headerTitle = headerView.findViewById(R.id.drawer_header_action_xmpp_accounts); + headerTitle.setOnClickListener(this); + + listView.addHeaderView(headerView); + + adapter = new NavigationDrawerAccountAdapter(getActivity()); + listView.setAdapter(adapter); + listView.setOnItemClickListener(this); + + footerView.findViewById(R.id.drawer_action_settings).setOnClickListener(this); + footerView.findViewById(R.id.drawer_action_about).setOnClickListener(this); + footerView.findViewById(R.id.drawer_action_exit).setOnClickListener(this); + + divider = footerView.findViewById(R.id.drawer_divider); + + + + return view; + } + + @Override + public void onResume() { + super.onResume(); + Application.getInstance().addUIListener(OnAccountChangedListener.class, this); + update(); + } + + @Override + public void onPause() { + super.onPause(); + Application.getInstance().removeUIListener(OnAccountChangedListener.class, this); + } + + @Override + public void onDetach() { + super.onDetach(); + listener = null; + } + + @Override + public void onClick(View v) { + listener.onContactListDrawerListener(v.getId()); + } + + @Override + public void onAccountsChanged(Collection accounts) { + update(); + } + + private void update() { + adapter.onChange(); + drawerHeaderImage.setImageLevel(AccountPainter.getDefaultAccountColorLevel()); + + if (adapter.getCount() == 0) { + headerTitle.setVisibility(View.GONE); + divider.setVisibility(View.GONE); + } else { + headerTitle.setVisibility(View.VISIBLE); + divider.setVisibility(View.VISIBLE); + } + } + + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + listener.onAccountSelected((String) listView.getItemAtPosition(position)); + } + + interface ContactListDrawerListener { + void onContactListDrawerListener(int viewId); + + void onAccountSelected(String account); + } +} diff --git a/app/src/main/java/com/xabber/android/ui/ContactListFragment.java b/app/src/main/java/com/xabber/android/ui/ContactListFragment.java new file mode 100644 index 0000000000..fa3b18dd84 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/ContactListFragment.java @@ -0,0 +1,467 @@ +package com.xabber.android.ui; + +import android.app.Activity; +import android.os.Bundle; +import android.os.SystemClock; +import android.support.v4.app.Fragment; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.widget.AdapterView; +import android.widget.AdapterView.AdapterContextMenuInfo; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.Button; +import android.widget.Filterable; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.PopupMenu; +import android.widget.TextView; + +import com.melnykov.fab.FloatingActionButton; +import com.xabber.android.R; +import com.xabber.android.data.Application; +import com.xabber.android.data.SettingsManager; +import com.xabber.android.data.account.AccountManager; +import com.xabber.android.data.account.CommonState; +import com.xabber.android.data.account.OnAccountChangedListener; +import com.xabber.android.data.account.StatusMode; +import com.xabber.android.data.connection.ConnectionManager; +import com.xabber.android.data.entity.BaseEntity; +import com.xabber.android.data.message.OnChatChangedListener; +import com.xabber.android.data.roster.AbstractContact; +import com.xabber.android.data.roster.OnContactChangedListener; +import com.xabber.android.ui.adapter.AccountActionButtonsAdapter; +import com.xabber.android.ui.adapter.AccountConfiguration; +import com.xabber.android.ui.adapter.ContactListAdapter; +import com.xabber.android.ui.adapter.ContactListAdapter.OnContactListChangedListener; +import com.xabber.android.ui.adapter.ContactListState; +import com.xabber.android.ui.adapter.GroupConfiguration; +import com.xabber.android.ui.adapter.GroupedContactAdapter; +import com.xabber.android.ui.adapter.UpdatableAdapter; +import com.xabber.android.ui.helper.AccountPainter; +import com.xabber.android.ui.helper.ContextMenuHelper; +import com.xabber.android.ui.preferences.AccountList; + +import java.util.Collection; + +public class ContactListFragment extends Fragment implements OnAccountChangedListener, + OnContactChangedListener, OnChatChangedListener, OnItemClickListener, + OnContactListChangedListener, View.OnClickListener, GroupedContactAdapter.OnClickListener { + + private ContactListAdapter adapter; + + private ListView listView; + + /** + * View with information shown on empty contact list. + */ + private View infoView; + + /** + * Image view with connected icon. + */ + private View connectedView; + + /** + * Image view with disconnected icon. + */ + private View disconnectedView; + + /** + * View with help text. + */ + private TextView textView; + + /** + * Button to apply help text. + */ + private Button buttonView; + + /** + * Animation for disconnected view. + */ + private Animation animation; + private AccountActionButtonsAdapter accountActionButtonsAdapter; + private View scrollToChatsActionButtonContainer; + private View actionButtonsContainer; + private FloatingActionButton scrollToChatsActionButton; + private AccountPainter accountPainter; + + private ContactListFragmentListener contactListFragmentListener; + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + + contactListFragmentListener = (ContactListFragmentListener) activity; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.contact_list_fragment, container, false); + listView = (ListView) view.findViewById(android.R.id.list); + listView.setOnItemClickListener(this); + listView.setItemsCanFocus(true); + registerForContextMenu(listView); + adapter = new ContactListAdapter(getActivity(), this, this); + listView.setAdapter(adapter); + infoView = view.findViewById(R.id.info); + connectedView = infoView.findViewById(R.id.connected); + disconnectedView = infoView.findViewById(R.id.disconnected); + textView = (TextView) infoView.findViewById(R.id.text); + buttonView = (Button) infoView.findViewById(R.id.button); + animation = AnimationUtils.loadAnimation(getActivity(), R.anim.connection); + + accountActionButtonsAdapter = new AccountActionButtonsAdapter(getActivity(), + this, (LinearLayout) view.findViewById(R.id.account_action_buttons)); + accountActionButtonsAdapter.onChange(); + + actionButtonsContainer = view.findViewById(R.id.account_action_buttons_container); + + scrollToChatsActionButtonContainer = view.findViewById(R.id.fab_up_container); + scrollToChatsActionButtonContainer.setOnClickListener(this); + scrollToChatsActionButtonContainer.setVisibility(View.GONE); + + scrollToChatsActionButton = (FloatingActionButton) view.findViewById(R.id.fab_up); + + accountPainter = new AccountPainter(getActivity()); + scrollToChatsActionButton.setColorNormal(accountPainter.getDefaultMainColor()); + scrollToChatsActionButton.setColorPressed(accountPainter.getDefaultDarkColor()); + + return view; + } + + @Override + public void onResume() { + super.onResume(); + Application.getInstance().addUIListener(OnAccountChangedListener.class, this); + Application.getInstance().addUIListener(OnContactChangedListener.class, this); + Application.getInstance().addUIListener(OnChatChangedListener.class, this); + adapter.onChange(); + scrollToChatsActionButton.setColorNormal(accountPainter.getDefaultMainColor()); + scrollToChatsActionButton.setColorPressed(accountPainter.getDefaultDarkColor()); + + if (SettingsManager.contactsShowPanel()) { + actionButtonsContainer.setVisibility(View.VISIBLE); + } else { + actionButtonsContainer.setVisibility(View.GONE); + } + } + + @Override + public void onPause() { + super.onPause(); + unregisterListeners(); + } + + @Override + public void onDetach() { + super.onDetach(); + contactListFragmentListener = null; + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) { + AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; + BaseEntity baseEntity = (BaseEntity) listView.getItemAtPosition(info.position); + if (baseEntity instanceof AbstractContact) { + ContextMenuHelper.createContactContextMenu( + getActivity(), adapter, (AbstractContact) baseEntity, menu); + } else if (baseEntity instanceof AccountConfiguration) { + ContextMenuHelper.createAccountContextMenu( + getActivity(), adapter, baseEntity.getAccount(), menu); + } else if (baseEntity instanceof GroupConfiguration) { + ContextMenuHelper.createGroupContextMenu(getActivity(), adapter, + baseEntity.getAccount(), baseEntity.getUser(), menu); + } else { + throw new IllegalStateException(); + } + } + + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + Object object = parent.getAdapter().getItem(position); + if (object instanceof AbstractContact) { + contactListFragmentListener.onContactClick((AbstractContact) object); + } else if (object instanceof GroupConfiguration) { + GroupConfiguration groupConfiguration = (GroupConfiguration) object; + adapter.setExpanded(groupConfiguration.getAccount(), groupConfiguration.getUser(), + !groupConfiguration.isExpanded()); + } + } + + @Override + public void onContactsChanged(Collection addresses) { + adapter.refreshRequest(); + } + + @Override + public void onAccountsChanged(Collection accounts) { + adapter.refreshRequest(); + scrollToChatsActionButton.setColorNormal(accountPainter.getDefaultMainColor()); + scrollToChatsActionButton.setColorPressed(accountPainter.getDefaultDarkColor()); + } + + @Override + public void onChatChanged(String account, String user, boolean incoming) { + if (incoming) { + adapter.refreshRequest(); + } + } + + @Override + public void onContactListChanged(CommonState commonState, boolean hasContacts, + boolean hasVisibleContacts, boolean isFilterEnabled) { + if (adapter.isHasActiveChats()) { + scrollToChatsActionButtonContainer.setVisibility(View.VISIBLE); + } else { + scrollToChatsActionButtonContainer.setVisibility(View.GONE); + } + + contactListFragmentListener.onContactListChange(commonState); + + if (hasVisibleContacts) { + infoView.setVisibility(View.GONE); + disconnectedView.clearAnimation(); + return; + } + infoView.setVisibility(View.VISIBLE); + final int text; + final int button; + final ContactListState state; + final View.OnClickListener listener; + if (isFilterEnabled) { + if (commonState == CommonState.online) { + state = ContactListState.online; + } else if (commonState == CommonState.roster || commonState == CommonState.connecting) { + state = ContactListState.connecting; + } else { + state = ContactListState.offline; + } + text = R.string.application_state_no_online; + button = 0; + listener = null; + } else if (hasContacts) { + state = ContactListState.online; + text = R.string.application_state_no_online; + button = R.string.application_action_no_online; + listener = new View.OnClickListener() { + @Override + public void onClick(View view) { + SettingsManager.setContactsShowOffline(true); + adapter.onChange(); + } + }; + } else if (commonState == CommonState.online) { + state = ContactListState.online; + text = R.string.application_state_no_contacts; + button = R.string.application_action_no_contacts; + listener = new View.OnClickListener() { + @Override + public void onClick(View view) { + startActivity(ContactAdd.createIntent(getActivity())); + } + }; + } else if (commonState == CommonState.roster) { + state = ContactListState.connecting; + text = R.string.application_state_roster; + button = 0; + listener = null; + } else if (commonState == CommonState.connecting) { + state = ContactListState.connecting; + text = R.string.application_state_connecting; + button = 0; + listener = null; + } else if (commonState == CommonState.waiting) { + state = ContactListState.offline; + text = R.string.application_state_waiting; + button = R.string.application_action_waiting; + listener = new View.OnClickListener() { + @Override + public void onClick(View view) { + ConnectionManager.getInstance().updateConnections(true); + } + }; + } else if (commonState == CommonState.offline) { + state = ContactListState.offline; + text = R.string.application_state_offline; + button = R.string.application_action_offline; + listener = new View.OnClickListener() { + @Override + public void onClick(View view) { + AccountManager.getInstance().setStatus( + StatusMode.available, null); + } + }; + } else if (commonState == CommonState.disabled) { + state = ContactListState.offline; + text = R.string.application_state_disabled; + button = R.string.application_action_disabled; + listener = new View.OnClickListener() { + @Override + public void onClick(View view) { + startActivity(AccountList.createIntent(getActivity())); + } + }; + } else if (commonState == CommonState.empty) { + state = ContactListState.offline; + text = R.string.application_state_empty; + button = R.string.application_action_empty; + listener = new View.OnClickListener() { + @Override + public void onClick(View view) { + startActivity(AccountAdd.createIntent(getActivity())); + } + }; + } else { + throw new IllegalStateException(); + } + if (state == ContactListState.offline) { + connectedView.setVisibility(View.INVISIBLE); + disconnectedView.setVisibility(View.VISIBLE); + disconnectedView.clearAnimation(); + } else if (state == ContactListState.connecting) { + connectedView.setVisibility(View.VISIBLE); + disconnectedView.setVisibility(View.VISIBLE); + if (disconnectedView.getAnimation() == null) { + disconnectedView.startAnimation(animation); + } + } else { + connectedView.setVisibility(View.VISIBLE); + disconnectedView.setVisibility(View.INVISIBLE); + disconnectedView.clearAnimation(); + } + textView.setText(text); + if (button == 0) { + buttonView.setVisibility(View.GONE); + } else { + buttonView.setVisibility(View.VISIBLE); + buttonView.setText(button); + } + buttonView.setOnClickListener(listener); + } + + /** + * Force stop contact list updates before pause or application close. + */ + void unregisterListeners() { + Application.getInstance().removeUIListener(OnAccountChangedListener.class, this); + Application.getInstance().removeUIListener(OnContactChangedListener.class, this); + Application.getInstance().removeUIListener(OnChatChangedListener.class, this); + adapter.removeRefreshRequests(); + } + + UpdatableAdapter getAdapter() { + return adapter; + } + + Filterable getFilterableAdapter() { + return adapter; + } + + /** + * Scroll contact list to specified account. + * + * @param account + */ + void scrollTo(String account) { + long count = listView.getCount(); + for (int position = 0; position < (int) count; position++) { + BaseEntity baseEntity = (BaseEntity) listView.getItemAtPosition(position); + if (baseEntity != null && baseEntity instanceof AccountConfiguration + && baseEntity.getAccount().equals(account)) { + stopMovement(); + listView.setSelection(position); + break; + } + } + } + + /** + * Filter out contact list for selected account. + * + * @param account + */ + void setSelectedAccount(String account) { + if (account.equals(AccountManager.getInstance().getSelectedAccount())) { + SettingsManager.setContactsSelectedAccount(""); + } else { + SettingsManager.setContactsSelectedAccount(account); + } + stopMovement(); + adapter.onChange(); + } + + /** + * Scroll to the top of contact list. + */ + void scrollUp() { + if (listView.getCount() > 0) { + listView.setSelection(0); + } + stopMovement(); + } + + /** + * Stop fling scrolling. + */ + private void stopMovement() { + MotionEvent event = MotionEvent.obtain(SystemClock.uptimeMillis(), + SystemClock.uptimeMillis(), MotionEvent.ACTION_CANCEL, 0, 0, 0); + listView.onTouchEvent(event); + event.recycle(); + } + + @Override + public void onClick(View view) { + if (view.getId() == R.id.fab_up_container) { + scrollUp(); + return; + } + + + String account = accountActionButtonsAdapter.getItemForView(view); + if (account == null) { // Check for tap on account in the title + return; + } + if (!SettingsManager.contactsShowAccounts()) { + if (AccountManager.getInstance().getAccounts().size() < 2) { + scrollUp(); + } else { + setSelectedAccount(account); + rebuild(); + } + } else { + scrollTo(account); + } + } + + public void onAccountsChanged() { + accountActionButtonsAdapter.onChange(); + } + + public void rebuild() { + accountActionButtonsAdapter.rebuild(); + } + + @Override + public void onAccountMenuClick(View view, final String account) { + PopupMenu popup = new PopupMenu(getActivity(), view); + popup.inflate(R.menu.account); + ContextMenuHelper.setUpAccountMenu(getActivity(), adapter, account, popup.getMenu()); + popup.show(); + } + + public interface ContactListFragmentListener { + void onContactClick(AbstractContact contact); + void onContactListChange(CommonState commonState); + } + + + +} diff --git a/app/src/main/java/com/xabber/android/ui/ContactSubscription.java b/app/src/main/java/com/xabber/android/ui/ContactSubscription.java new file mode 100644 index 0000000000..6cf41f3429 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/ContactSubscription.java @@ -0,0 +1,145 @@ +/** + * Copyright (c) 2013, Redsolution LTD. All rights reserved. + * + * This file is part of Xabber project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License, Version 3. + * + * Xabber is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License, + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.xabber.android.ui; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.widget.Toolbar; +import android.view.View; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.TextView; + +import com.xabber.android.R; +import com.xabber.android.data.Application; +import com.xabber.android.data.NetworkException; +import com.xabber.android.data.intent.EntityIntentBuilder; +import com.xabber.android.data.roster.AbstractContact; +import com.xabber.android.data.roster.PresenceManager; +import com.xabber.android.data.roster.RosterManager; +import com.xabber.android.data.roster.SubscriptionRequest; +import com.xabber.android.ui.helper.AccountPainter; +import com.xabber.android.ui.helper.BarPainter; +import com.xabber.android.ui.helper.SingleActivity; + +public class ContactSubscription extends SingleActivity implements View.OnClickListener { + + private String account; + private String user; + private SubscriptionRequest subscriptionRequest; + + public static Intent createIntent(Context context, String account, + String user) { + return new EntityIntentBuilder(context, ContactSubscription.class) + .setAccount(account).setUser(user).build(); + } + + private static String getAccount(Intent intent) { + return EntityIntentBuilder.getAccount(intent); + } + + private static String getUser(Intent intent) { + return EntityIntentBuilder.getUser(intent); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Intent intent = getIntent(); + account = getAccount(intent); + user = getUser(intent); + subscriptionRequest = PresenceManager.getInstance().getSubscriptionRequest(account, user); + if (subscriptionRequest == null) { + Application.getInstance().onError(R.string.ENTRY_IS_NOT_FOUND); + finish(); + return; + } + + setContentView(R.layout.contact_subscription); + Toolbar toolbar = (Toolbar) findViewById(R.id.top_toolbar); + + setSupportActionBar(toolbar); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + toolbar.setTitle(getString(R.string.subscription_request_message)); + + BarPainter barPainter = new BarPainter(this, toolbar); + barPainter.updateWithAccountName(account); + AccountPainter accountPainter = new AccountPainter(this); + + View fakeToolbar = findViewById(R.id.fake_toolbar); + + fakeToolbar.setBackgroundColor(accountPainter.getAccountMainColor(account)); + toolbar.setBackgroundResource(android.R.color.transparent); + + AbstractContact abstractContact = RosterManager.getInstance().getBestContact(account, user); + + ((ImageView)fakeToolbar.findViewById(R.id.avatar)).setImageDrawable(abstractContact.getAvatar()); + + ((TextView)fakeToolbar.findViewById(R.id.dialog_message)).setText(subscriptionRequest.getConfirmation()); + + Button acceptButton = (Button) findViewById(R.id.accept_button); + acceptButton.setTextColor(accountPainter.getAccountMainColor(account)); + acceptButton.setOnClickListener(this); + + findViewById(R.id.decline_button).setOnClickListener(this); + + + if (savedInstanceState == null) { + getFragmentManager().beginTransaction() + .add(R.id.scrollable_container, ContactVcardViewerFragment.newInstance(account, user)).commit(); + } + + + } + + @Override + public void onClick(View v) { + switch (v.getId()) { + case R.id.accept_button: + onAccept(); + break; + + case R.id.decline_button: + onDecline(); + break; + } + } + + public void onAccept() { + try { + PresenceManager.getInstance().acceptSubscription( + subscriptionRequest.getAccount(), + subscriptionRequest.getUser()); + } catch (NetworkException e) { + Application.getInstance().onError(e); + } + startActivity(ContactAdd.createIntent(this, account, user)); + finish(); + } + + public void onDecline() { + try { + PresenceManager.getInstance().discardSubscription( + subscriptionRequest.getAccount(), + subscriptionRequest.getUser()); + } catch (NetworkException e) { + Application.getInstance().onError(e); + } + finish(); + } + +} diff --git a/app/src/main/java/com/xabber/android/ui/ContactVcardViewerFragment.java b/app/src/main/java/com/xabber/android/ui/ContactVcardViewerFragment.java new file mode 100644 index 0000000000..8d77e1b43c --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/ContactVcardViewerFragment.java @@ -0,0 +1,454 @@ +package com.xabber.android.ui; + +import android.app.Activity; +import android.app.Fragment; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.text.util.Linkify; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.xabber.android.R; +import com.xabber.android.data.Application; +import com.xabber.android.data.LogManager; +import com.xabber.android.data.VcardMaps; +import com.xabber.android.data.account.OnAccountChangedListener; +import com.xabber.android.data.entity.BaseEntity; +import com.xabber.android.data.extension.capability.CapabilitiesManager; +import com.xabber.android.data.extension.capability.ClientInfo; +import com.xabber.android.data.extension.vcard.OnVCardListener; +import com.xabber.android.data.extension.vcard.VCardManager; +import com.xabber.android.data.roster.OnContactChangedListener; +import com.xabber.android.data.roster.PresenceManager; +import com.xabber.android.data.roster.ResourceItem; +import com.xabber.xmpp.vcard.Address; +import com.xabber.xmpp.vcard.AddressProperty; +import com.xabber.xmpp.vcard.AddressType; +import com.xabber.xmpp.vcard.Email; +import com.xabber.xmpp.vcard.EmailType; +import com.xabber.xmpp.vcard.NameProperty; +import com.xabber.xmpp.vcard.Organization; +import com.xabber.xmpp.vcard.Telephone; +import com.xabber.xmpp.vcard.TelephoneType; +import com.xabber.xmpp.vcard.VCard; +import com.xabber.xmpp.vcard.VCardProperty; +import com.xabber.xmpp.vcard.VCardProvider; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserFactory; + +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class ContactVcardViewerFragment extends Fragment implements OnVCardListener, OnContactChangedListener, OnAccountChangedListener { + public static final String ARGUMENT_ACCOUNT = "com.xabber.android.ui.ContactVcardViewerFragment.ARGUMENT_ACCOUNT"; + public static final String ARGUMENT_USER = "com.xabber.android.ui.ContactVcardViewerFragment.ARGUMENT_USER"; + private static final String SAVED_VCARD = "com.xabber.android.ui.ContactVcardViewerFragment.SAVED_VCARD"; + private static final String SAVED_VCARD_ERROR = "com.xabber.android.ui.ContactVcardViewerFragment.SAVED_VCARD_ERROR"; + String account; + String user; + private LinearLayout xmppItems; + private LinearLayout contactInfoItems; + private VCard vCard; + private boolean vCardError; + + public static ContactVcardViewerFragment newInstance(String account, String user) { + ContactVcardViewerFragment fragment = new ContactVcardViewerFragment(); + + Bundle arguments = new Bundle(); + arguments.putString(ARGUMENT_ACCOUNT, account); + arguments.putString(ARGUMENT_USER, user); + fragment.setArguments(arguments); + return fragment; + } + + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Bundle args = getArguments(); + account = args.getString(ARGUMENT_ACCOUNT, null); + user = args.getString(ARGUMENT_USER, null); + + vCard = null; + vCardError = false; + if (savedInstanceState != null) { + vCardError = savedInstanceState.getBoolean(SAVED_VCARD_ERROR, false); + String xml = savedInstanceState.getString(SAVED_VCARD); + if (xml != null) + try { + XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser(); + parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); + parser.setInput(new StringReader(xml)); + int eventType = parser.next(); + if (eventType != XmlPullParser.START_TAG) { + throw new IllegalStateException(String.valueOf(eventType)); + } + if (!VCard.ELEMENT_NAME.equals(parser.getName())) { + throw new IllegalStateException(parser.getName()); + } + if (!VCard.NAMESPACE.equals(parser.getNamespace())) { + throw new IllegalStateException(parser.getNamespace()); + } + vCard = (VCard) (new VCardProvider()).parseIQ(parser); + } catch (Exception e) { + LogManager.exception(this, e); + } + } + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + super.onCreateView(inflater, container, savedInstanceState); + + View view = inflater.inflate(R.layout.contact_vcard_viewer_fragment, container, false); + + xmppItems = (LinearLayout) view.findViewById(R.id.xmpp_items); + contactInfoItems = (LinearLayout) view.findViewById(R.id.contact_info_items); + + return view; + } + + @Override + public void onResume() { + super.onResume(); + Application.getInstance().addUIListener(OnVCardListener.class, this); + Application.getInstance().addUIListener(OnContactChangedListener.class, this); + Application.getInstance().addUIListener(OnAccountChangedListener.class, this); + if (vCard == null && !vCardError) { + VCardManager.getInstance().request(account, user, null); + } + + updateContact(account, user); + updateVCard(vCard); + } + + @Override + public void onPause() { + super.onPause(); + Application.getInstance().removeUIListener(OnVCardListener.class, this); + Application.getInstance().removeUIListener(OnContactChangedListener.class, this); + Application.getInstance().removeUIListener(OnAccountChangedListener.class, this); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putBoolean(SAVED_VCARD_ERROR, vCardError); + if (vCard != null) { + outState.putString(SAVED_VCARD, vCard.getChildElementXML()); + } + } + + @Override + public void onVCardReceived(String account, String bareAddress, VCard vCard) { + if (!this.account.equals(account) || !this.user.equals(bareAddress)) { + return; + } + this.vCard = vCard; + this.vCardError = false; + updateVCard(vCard); + } + + @Override + public void onVCardFailed(String account, String bareAddress) { + if (!this.account.equals(account) || !this.user.equals(bareAddress)) { + return; + } + this.vCard = null; + this.vCardError = true; + Application.getInstance().onError(R.string.XMPP_EXCEPTION); + } + + @Override + public void onContactsChanged(Collection entities) { + for (BaseEntity entity : entities) { + if (entity.equals(account, user)) { + updateContact(account, user); + break; + } + } + } + + @Override + public void onAccountsChanged(Collection accounts) { + if (accounts.contains(account)) { + updateContact(account, user); + } + } + + /** + * @param source + * @param value + * @param splitter + * @return Concatenated source and value with splitter if necessary. + */ + private String addString(String source, String value, String splitter) { + if (value == null || "".equals(value)) { + return source; + } + if (source == null || "".equals(source)) { + return value; + } + return source + splitter + value; + } + + public void updateContact(String account, String bareAddress) { + this.account = account; + this.user = bareAddress; + + xmppItems.removeAllViews(); + + View jabberIdView = createItemView(xmppItems, getString(R.string.jabber_id), + bareAddress, R.drawable.ic_vcard_xmpp_24dp); + + if (jabberIdView != null) { + xmppItems.addView(jabberIdView); + } + + List resourcesList = new ArrayList<>(); + + for (ResourceItem resourceItem + : PresenceManager.getInstance().getResourceItems(account, bareAddress)) { + + String user = resourceItem.getUser(bareAddress); + ClientInfo clientInfo = CapabilitiesManager.getInstance().getClientInfo(account, user); + + String client = ""; + if (clientInfo == null) { + CapabilitiesManager.getInstance().request(account, user); + client = getString(R.string.please_wait); + } else if (clientInfo == CapabilitiesManager.INVALID_CLIENT_INFO) { + client = getString(R.string.unknown); + } else { + String name = clientInfo.getName(); + if (name != null) { + client = name; + } + + String type = clientInfo.getType(); + if (type != null) { + if (client.isEmpty()) { + client = type; + } else { + client = client + "/" + type; + } + } + } + + String priority = getString(R.string.account_priority) + ": " + resourceItem.getPriority(); + + String label = ""; + if (!client.isEmpty()) { + label = getString(R.string.contact_viewer_client) + ": " + client + ", "; + } + + label += priority; + + String resource = getString(R.string.account_resource) + ": " + resourceItem.getVerbose(); + + String status = resourceItem.getStatusText().trim(); + if (status.isEmpty()) { + status = getString(resourceItem.getStatusMode().getStringID()); + } + + LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Activity.LAYOUT_INFLATER_SERVICE); + + View resourceView = inflater.inflate(R.layout.contact_info_item, xmppItems, false); + + ((TextView)resourceView.findViewById(R.id.contact_info_item_secondary)).setText(label); + ((TextView)resourceView.findViewById(R.id.contact_info_item_main)).setText(status); + + ((TextView)resourceView.findViewById(R.id.contact_info_item_secondary_second_line)).setText(resource); + resourceView.findViewById(R.id.contact_info_item_secondary_second_line).setVisibility(View.VISIBLE); + + ImageView statusIcon = (ImageView) resourceView.findViewById(R.id.contact_info_right_icon); + statusIcon.setVisibility(View.VISIBLE); + + statusIcon.setImageDrawable(getResources().getDrawable(R.drawable.ic_status)); + statusIcon.setImageLevel(resourceItem.getStatusMode().getStatusLevel()); + + resourcesList.add(resourceView); + } + + addItemGroup(resourcesList, xmppItems, R.drawable.ic_vcard_jabber_24dp); + } + + public void updateVCard(VCard vCard) { + if (vCard == null) { + return; + } + + contactInfoItems.removeAllViews(); + + addNameInfo(vCard); + + List birthDayList = new ArrayList<>(); + addItem(birthDayList, contactInfoItems, getString(R.string.vcard_birth_date), vCard.getField(VCardProperty.BDAY)); + addItemGroup(birthDayList, contactInfoItems, R.drawable.ic_vcard_birthday_24dp); + + addOrganizationInfo(vCard); + + List webList = new ArrayList<>(); + addItem(webList, contactInfoItems, getString(R.string.vcard_url), vCard.getField(VCardProperty.URL)); + addItemGroup(webList, contactInfoItems, R.drawable.ic_vcard_web_24dp); + + addAdditionalInfo(vCard); + addAddresses(vCard); + addPhones(vCard); + addEmails(vCard); + } + + private void addEmails(VCard vCard) { + List emailList = new ArrayList<>(); + for (Email email : vCard.getEmails()) { + String types = null; + for (EmailType type : email.getTypes()) { + types = addString(types, getString(VcardMaps.getEmailTypeMap().get(type)), ", "); + } + + addItem(emailList, contactInfoItems, types, email.getValue()); + } + + addItemGroup(emailList, contactInfoItems, R.drawable.ic_vcard_email_24dp); + } + + private void addPhones(VCard vCard) { + List phoneList = new ArrayList<>(); + for (Telephone telephone : vCard.getTelephones()) { + String types = null; + for (TelephoneType type : telephone.getTypes()) { + types = addString(types, getString(VcardMaps.getTelephoneTypeMap().get(type)), ", "); + } + + addItem(phoneList, contactInfoItems, types, telephone.getValue()); + } + + addItemGroup(phoneList, contactInfoItems, R.drawable.ic_vcard_phone_24dp); + } + + private void addAddresses(VCard vCard) { + List addressList = new ArrayList<>(); + + for (Address address : vCard.getAddresses()) { + String types = null; + for (AddressType type : address.getTypes()) { + types = addString(types, getString(VcardMaps.getAddressTypeMap().get(type)), ", "); + } + + String value = null; + for (AddressProperty property : AddressProperty.values()) { + value = addString(value, address.getProperties().get(property), "\n"); + } + + addItem(addressList, contactInfoItems, types, value); + } + + addItemGroup(addressList, contactInfoItems, R.drawable.ic_vcard_address_24dp); + } + + private void addAdditionalInfo(VCard vCard) { + String categories = null; + for (String category : vCard.getCategories()) { + categories = addString(categories, category, "\n"); + } + + List notesList = new ArrayList<>(); + addItem(notesList, contactInfoItems, getString(R.string.vcard_categories), categories); + addItem(notesList, contactInfoItems, getString(R.string.vcard_note), vCard.getField(VCardProperty.NOTE)); + addItem(notesList, contactInfoItems, getString(R.string.vcard_decsription), vCard.getField(VCardProperty.DESC)); + addItemGroup(notesList, contactInfoItems, R.drawable.ic_vcard_notes_24dp); + } + + private void addOrganizationInfo(VCard vCard) { + List organizationList = new ArrayList<>(); + + addItem(organizationList, contactInfoItems, getString(R.string.vcard_title), vCard.getField(VCardProperty.TITLE)); + addItem(organizationList, contactInfoItems, getString(R.string.vcard_role), vCard.getField(VCardProperty.ROLE)); + + List organizations = vCard.getOrganizations(); + String organization; + if (organizations.isEmpty()) { + organization = null; + } else { + organization = organizations.get(0).getName(); + for (String unit : organizations.get(0).getUnits()) { + organization = addString(organization, unit, "\n"); + } + } + addItem(organizationList, contactInfoItems, getString(R.string.vcard_organization), organization); + + addItemGroup(organizationList, contactInfoItems, R.drawable.ic_vcard_job_title_24dp); + } + + private void addNameInfo(VCard vCard) { + List nameList = new ArrayList<>(); + + addItem(nameList, contactInfoItems, getString(R.string.vcard_nick_name), vCard.getField(VCardProperty.NICKNAME)); + addItem(nameList, contactInfoItems, getString(R.string.vcard_formatted_name), vCard.getFormattedName()); + addItem(nameList, contactInfoItems, getString(R.string.vcard_prefix_name), vCard.getField(NameProperty.PREFIX)); + addItem(nameList, contactInfoItems, getString(R.string.vcard_given_name), vCard.getField(NameProperty.GIVEN)); + addItem(nameList, contactInfoItems, getString(R.string.vcard_middle_name), vCard.getField(NameProperty.MIDDLE)); + addItem(nameList, contactInfoItems, getString(R.string.vcard_family_name), vCard.getField(NameProperty.FAMILY)); + addItem(nameList, contactInfoItems, getString(R.string.vcard_suffix_name), vCard.getField(NameProperty.SUFFIX)); + + addItemGroup(nameList, contactInfoItems, R.drawable.ic_vcard_contact_info_24dp); + } + + private void addItemGroup(List nameList, LinearLayout itemList, int groupIcon) { + if (nameList.isEmpty()) { + return; + } + + addSeparator(itemList); + ((ImageView) nameList.get(0).findViewById(R.id.contact_info_group_icon)).setImageResource(groupIcon); + + for (View view : nameList) { + itemList.addView(view); + } + } + + private void addItem(List nameList, ViewGroup rootView, String label, String value) { + View itemView = createItemView(rootView, label, value, null); + if (itemView != null) { + Linkify.addLinks((TextView)itemView.findViewById(R.id.contact_info_item_main), Linkify.ALL); + nameList.add(itemView); + } + } + + private void addSeparator(LinearLayout rootView) { + LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Activity.LAYOUT_INFLATER_SERVICE); + rootView.addView(inflater.inflate(R.layout.contact_info_separator, rootView, false)); + } + + private View createItemView(ViewGroup rootView, String label, String value, Integer iconResource) { + if (value == null || value.isEmpty() ) { + return null; + } + + LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Activity.LAYOUT_INFLATER_SERVICE); + + View contactInfoItem = inflater.inflate(R.layout.contact_info_item, rootView, false); + + if (label == null || label.trim().isEmpty()) { + contactInfoItem.findViewById(R.id.contact_info_item_secondary).setVisibility(View.GONE); + } else { + ((TextView) contactInfoItem.findViewById(R.id.contact_info_item_secondary)).setText(label); + } + ((TextView)contactInfoItem.findViewById(R.id.contact_info_item_main)).setText(value); + + if (iconResource != null) { + ((ImageView) contactInfoItem.findViewById(R.id.contact_info_group_icon)).setImageResource(iconResource); + } + return contactInfoItem; + } + +} diff --git a/app/src/main/java/com/xabber/android/ui/ContactViewer.java b/app/src/main/java/com/xabber/android/ui/ContactViewer.java index 79efc4e3c7..ae40f7f473 100644 --- a/app/src/main/java/com/xabber/android/ui/ContactViewer.java +++ b/app/src/main/java/com/xabber/android/ui/ContactViewer.java @@ -1,454 +1,173 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.ui; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserFactory; - import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Bundle; -import android.preference.PreferenceCategory; -import android.preference.PreferenceGroup; -import android.preference.PreferenceScreen; +import android.support.v4.app.NavUtils; +import android.support.v7.widget.Toolbar; +import android.view.View; +import android.widget.TextView; +import com.xabber.android.R; import com.xabber.android.data.Application; -import com.xabber.android.data.LogManager; +import com.xabber.android.data.account.AccountManager; import com.xabber.android.data.account.OnAccountChangedListener; import com.xabber.android.data.entity.BaseEntity; -import com.xabber.android.data.extension.capability.CapabilitiesManager; -import com.xabber.android.data.extension.capability.ClientInfo; -import com.xabber.android.data.extension.vcard.OnVCardListener; -import com.xabber.android.data.extension.vcard.VCardManager; import com.xabber.android.data.intent.AccountIntentBuilder; import com.xabber.android.data.intent.EntityIntentBuilder; +import com.xabber.android.data.roster.AbstractContact; +import com.xabber.android.data.roster.GroupManager; import com.xabber.android.data.roster.OnContactChangedListener; -import com.xabber.android.data.roster.PresenceManager; -import com.xabber.android.data.roster.ResourceItem; import com.xabber.android.data.roster.RosterContact; import com.xabber.android.data.roster.RosterManager; -import com.xabber.android.ui.helper.ManagedPreferenceActivity; -import com.xabber.android.ui.widget.StatusPreference; -import com.xabber.androiddev.R; +import com.xabber.android.ui.helper.ContactTitleExpandableToolbarInflater; +import com.xabber.android.ui.helper.ManagedActivity; import com.xabber.xmpp.address.Jid; -import com.xabber.xmpp.vcard.Address; -import com.xabber.xmpp.vcard.AddressProperty; -import com.xabber.xmpp.vcard.AddressType; -import com.xabber.xmpp.vcard.Email; -import com.xabber.xmpp.vcard.EmailType; -import com.xabber.xmpp.vcard.NameProperty; -import com.xabber.xmpp.vcard.Organization; -import com.xabber.xmpp.vcard.Telephone; -import com.xabber.xmpp.vcard.TelephoneType; -import com.xabber.xmpp.vcard.VCard; -import com.xabber.xmpp.vcard.VCardProperty; -import com.xabber.xmpp.vcard.VCardProvider; - -public class ContactViewer extends ManagedPreferenceActivity implements - OnVCardListener, OnContactChangedListener, OnAccountChangedListener { - - private static final String SAVED_VCARD = "com.xabber.android.ui.ContactViewer.SAVED_VCARD"; - private static final String SAVED_VCARD_ERROR = "com.xabber.android.ui.ContactViewer.SAVED_VCARD_ERROR"; - - private String account; - private String bareAddress; - private VCard vCard; - private boolean vCardError; - private List addresses; - private List telephones; - private List emails; - - private static final Map ADDRESS_TYPE_MAP = new HashMap(); - private static final Map ADDRESS_PROPERTY_MAP = new HashMap(); - private static final Map TELEPHONE_TYPE_MAP = new HashMap(); - private static final Map EMAIL_TYPE_MAP = new HashMap(); - - static { - ADDRESS_TYPE_MAP.put(AddressType.DOM, R.string.vcard_type_dom); - ADDRESS_TYPE_MAP.put(AddressType.HOME, R.string.vcard_type_home); - ADDRESS_TYPE_MAP.put(AddressType.INTL, R.string.vcard_type_intl); - ADDRESS_TYPE_MAP.put(AddressType.PARCEL, R.string.vcard_type_parcel); - ADDRESS_TYPE_MAP.put(AddressType.POSTAL, R.string.vcard_type_postal); - ADDRESS_TYPE_MAP.put(AddressType.PREF, R.string.vcard_type_pref); - ADDRESS_TYPE_MAP.put(AddressType.WORK, R.string.vcard_type_work); - if (ADDRESS_TYPE_MAP.size() != AddressType.values().length) - throw new IllegalStateException(); - - ADDRESS_PROPERTY_MAP.put(AddressProperty.CTRY, - R.string.vcard_address_ctry); - ADDRESS_PROPERTY_MAP.put(AddressProperty.EXTADR, - R.string.vcard_address_extadr); - ADDRESS_PROPERTY_MAP.put(AddressProperty.LOCALITY, - R.string.vcard_address_locality); - ADDRESS_PROPERTY_MAP.put(AddressProperty.PCODE, - R.string.vcard_address_pcode); - ADDRESS_PROPERTY_MAP.put(AddressProperty.POBOX, - R.string.vcard_address_pobox); - ADDRESS_PROPERTY_MAP.put(AddressProperty.REGION, - R.string.vcard_address_region); - ADDRESS_PROPERTY_MAP.put(AddressProperty.STREET, - R.string.vcard_address_street); - if (ADDRESS_PROPERTY_MAP.size() != AddressProperty.values().length) - throw new IllegalStateException(); - - TELEPHONE_TYPE_MAP.put(TelephoneType.BBS, R.string.vcard_type_bbs); - TELEPHONE_TYPE_MAP.put(TelephoneType.CELL, R.string.vcard_type_cell); - TELEPHONE_TYPE_MAP.put(TelephoneType.FAX, R.string.vcard_type_fax); - TELEPHONE_TYPE_MAP.put(TelephoneType.HOME, R.string.vcard_type_home); - TELEPHONE_TYPE_MAP.put(TelephoneType.ISDN, R.string.vcard_type_isdn); - TELEPHONE_TYPE_MAP.put(TelephoneType.MODEM, R.string.vcard_type_modem); - TELEPHONE_TYPE_MAP.put(TelephoneType.MSG, R.string.vcard_type_msg); - TELEPHONE_TYPE_MAP.put(TelephoneType.PAGER, R.string.vcard_type_pager); - TELEPHONE_TYPE_MAP.put(TelephoneType.PCS, R.string.vcard_type_pcs); - TELEPHONE_TYPE_MAP.put(TelephoneType.PREF, R.string.vcard_type_pref); - TELEPHONE_TYPE_MAP.put(TelephoneType.VIDEO, R.string.vcard_type_video); - TELEPHONE_TYPE_MAP.put(TelephoneType.VOICE, R.string.vcard_type_voice); - TELEPHONE_TYPE_MAP.put(TelephoneType.WORK, R.string.vcard_type_work); - if (TELEPHONE_TYPE_MAP.size() != TelephoneType.values().length) - throw new IllegalStateException(); - - EMAIL_TYPE_MAP.put(EmailType.HOME, R.string.vcard_type_home); - EMAIL_TYPE_MAP.put(EmailType.INTERNET, R.string.vcard_type_internet); - EMAIL_TYPE_MAP.put(EmailType.PREF, R.string.vcard_type_pref); - EMAIL_TYPE_MAP.put(EmailType.WORK, R.string.vcard_type_work); - EMAIL_TYPE_MAP.put(EmailType.X400, R.string.vcard_type_x400); - if (EMAIL_TYPE_MAP.size() != EmailType.values().length) - throw new IllegalStateException(); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.contact_viewer); - addresses = new ArrayList(); - telephones = new ArrayList(); - emails = new ArrayList(); - - if (Intent.ACTION_VIEW.equals(getIntent().getAction())) { - // View information about contact from system contact list - Uri data = getIntent().getData(); - if (data != null && "content".equals(data.getScheme())) { - List segments = data.getPathSegments(); - if (segments.size() == 2 && "data".equals(segments.get(0))) { - Long id; - try { - id = Long.valueOf(segments.get(1)); - } catch (NumberFormatException e) { - id = null; - } - if (id != null) - // FIXME: Will be empty while application is loading - for (RosterContact rosterContact : RosterManager - .getInstance().getContacts()) - if (id.equals(rosterContact.getViewId())) { - account = rosterContact.getAccount(); - bareAddress = rosterContact.getUser(); - break; - } - } - } - } else { - account = getAccount(getIntent()); - bareAddress = Jid.getBareAddress(getUser(getIntent())); - } - if (account == null || bareAddress == null) { - Application.getInstance().onError(R.string.ENTRY_IS_NOT_FOUND); - finish(); - return; - } - vCard = null; - vCardError = false; - if (savedInstanceState != null) { - vCardError = savedInstanceState - .getBoolean(SAVED_VCARD_ERROR, false); - String xml = savedInstanceState.getString(SAVED_VCARD); - if (xml != null) - try { - XmlPullParser parser = XmlPullParserFactory.newInstance() - .newPullParser(); - parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, - true); - parser.setInput(new StringReader(xml)); - int eventType = parser.next(); - if (eventType != XmlPullParser.START_TAG) - throw new IllegalStateException( - String.valueOf(eventType)); - if (!VCard.ELEMENT_NAME.equals(parser.getName())) - throw new IllegalStateException(parser.getName()); - if (!VCard.NAMESPACE.equals(parser.getNamespace())) - throw new IllegalStateException(parser.getNamespace()); - vCard = (VCard) (new VCardProvider()).parseIQ(parser); - } catch (Exception e) { - LogManager.exception(this, e); - } - } - setTitle(getString(R.string.contact_viewer_for, bareAddress)); - } - - @Override - protected void onResume() { - super.onResume(); - Application.getInstance().addUIListener(OnVCardListener.class, this); - Application.getInstance().addUIListener(OnContactChangedListener.class, - this); - Application.getInstance().addUIListener(OnAccountChangedListener.class, - this); - if (vCard == null && !vCardError) - VCardManager.getInstance().request(account, bareAddress, null); - updateContact(); - updateVCard(); - } - - @Override - protected void onPause() { - super.onPause(); - Application.getInstance().removeUIListener(OnVCardListener.class, this); - Application.getInstance().removeUIListener( - OnContactChangedListener.class, this); - Application.getInstance().removeUIListener( - OnAccountChangedListener.class, this); - } - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putBoolean(SAVED_VCARD_ERROR, vCardError); - if (vCard != null) - outState.putString(SAVED_VCARD, vCard.getChildElementXML()); - } - - @Override - public void onVCardReceived(String account, String bareAddress, VCard vCard) { - if (!this.account.equals(account) - || !this.bareAddress.equals(bareAddress)) - return; - this.vCard = vCard; - this.vCardError = false; - updateVCard(); - } - - @Override - public void onVCardFailed(String account, String bareAddress) { - if (!this.account.equals(account) - || !this.bareAddress.equals(bareAddress)) - return; - this.vCard = null; - this.vCardError = true; - updateVCard(); - Application.getInstance().onError(R.string.XMPP_EXCEPTION); - } - - @Override - public void onContactsChanged(Collection entities) { - for (BaseEntity entity : entities) - if (entity.equals(account, bareAddress)) { - updateContact(); - break; - } - } - - @Override - public void onAccountsChanged(Collection accounts) { - if (accounts.contains(account)) - updateContact(); - } - - /** - * Sets value for the preference by its id. - * - * @param resourceId - * @param value - */ - private void setValue(int resourceId, String value) { - if (value == null) - value = ""; - findPreference(getString(resourceId)).setSummary(value); - } - - /** - * @param source - * @param value - * @param splitter - * @return Concatenated source and value with splitter if necessary. - */ - private String addString(String source, String value, String splitter) { - if (value == null || "".equals(value)) - return source; - if (source == null || "".equals(source)) - return value; - return source + splitter + value; - } - - private void updateContact() { - setValue(R.string.contact_viewer_jid, bareAddress); - RosterContact rosterContact = RosterManager.getInstance() - .getRosterContact(account, bareAddress); - setValue(R.string.contact_viewer_name, rosterContact == null ? null - : rosterContact.getRealName()); - PreferenceCategory preferenceCategory = (PreferenceCategory) findPreference(getString(R.string.contact_viewer_resources)); - preferenceCategory.removeAll(); - if (rosterContact != null && rosterContact.isConnected()) - for (ResourceItem resourceItem : PresenceManager.getInstance() - .getResourceItems(account, bareAddress)) { - StatusPreference preference = new StatusPreference(this); - preference.setLayoutResource(R.layout.info_preference); - preference.setStatusMode(resourceItem.getStatusMode()); - String user = resourceItem.getUser(bareAddress); - ClientInfo clientInfo = CapabilitiesManager.getInstance() - .getClientInfo(account, user); - String client; - if (clientInfo == null) { - CapabilitiesManager.getInstance().request(account, user); - client = getString(R.string.please_wait); - } else if (clientInfo == CapabilitiesManager.INVALID_CLIENT_INFO) { - client = getString(R.string.unknown); - } else { - String name = clientInfo.getName(); - if (name == null) - name = getString(R.string.unknown); - String type = clientInfo.getType(); - if (type == null) - type = getString(R.string.unknown); - client = getString(R.string.contact_viewer_client_info, - name, type); - } - preference.setTitle(getString( - R.string.contact_viewer_resource_summary, - resourceItem.getVerbose(), resourceItem.getPriority(), - client)); - preference.setSummary(resourceItem.getStatusText()); - preferenceCategory.addPreference(preference); - } - } - - private void updateVCard() { - if (vCard == null) - return; - setValue(R.string.vcard_nick_name, - vCard.getField(VCardProperty.NICKNAME)); - setValue(R.string.vcard_formatted_name, vCard.getFormattedName()); - setValue(R.string.vcard_prefix_name, - vCard.getField(NameProperty.PREFIX)); - setValue(R.string.vcard_given_name, vCard.getField(NameProperty.GIVEN)); - setValue(R.string.vcard_middle_name, - vCard.getField(NameProperty.MIDDLE)); - setValue(R.string.vcard_family_name, - vCard.getField(NameProperty.FAMILY)); - setValue(R.string.vcard_suffix_name, - vCard.getField(NameProperty.SUFFIX)); - setValue(R.string.vcard_birth_date, vCard.getField(VCardProperty.BDAY)); - setValue(R.string.vcard_title, vCard.getField(VCardProperty.TITLE)); - setValue(R.string.vcard_role, vCard.getField(VCardProperty.ROLE)); - List organizations = vCard.getOrganizations(); - String organization; - if (organizations.isEmpty()) - organization = null; - else { - organization = organizations.get(0).getName(); - for (String unit : organizations.get(0).getUnits()) - organization = addString(organization, unit, "\n"); - } - setValue(R.string.vcard_organization, organization); - setValue(R.string.vcard_url, vCard.getField(VCardProperty.URL)); - String categories = null; - for (String category : vCard.getCategories()) - categories = addString(categories, category, "\n"); - setValue(R.string.vcard_categories, categories); - setValue(R.string.vcard_note, vCard.getField(VCardProperty.NOTE)); - setValue(R.string.vcard_decsription, vCard.getField(VCardProperty.DESC)); - PreferenceScreen screen = getPreferenceScreen(); - for (PreferenceCategory category : addresses) - screen.removePreference(category); - for (PreferenceCategory category : telephones) - screen.removePreference(category); - for (PreferenceCategory category : emails) - screen.removePreference(category); - for (Address address : vCard.getAddresses()) { - String types = null; - for (AddressType type : address.getTypes()) - types = addString(types, getString(ADDRESS_TYPE_MAP.get(type)), - ", "); - String value = null; - for (AddressProperty property : AddressProperty.values()) - value = addString(value, address.getProperties().get(property), - "\n"); - PreferenceScreen addressScreen = createTypedCategory( - R.string.vcard_address, types, value); - for (AddressProperty property : AddressProperty.values()) - addPreferenceScreen(addressScreen, - ADDRESS_PROPERTY_MAP.get(property), address - .getProperties().get(property)); - } - for (Telephone telephone : vCard.getTelephones()) { - String types = null; - for (TelephoneType type : telephone.getTypes()) - types = addString(types, - getString(TELEPHONE_TYPE_MAP.get(type)), ", "); - createTypedCategory(R.string.vcard_telephone, types, - telephone.getValue()); - } - for (Email email : vCard.getEmails()) { - String types = null; - for (EmailType type : email.getTypes()) - types = addString(types, getString(EMAIL_TYPE_MAP.get(type)), - ", "); - createTypedCategory(R.string.vcard_email, types, email.getValue()); - } - } - - private PreferenceScreen createTypedCategory(int title, String types, - String value) { - PreferenceCategory preferenceCategory = new PreferenceCategory(this); - preferenceCategory.setTitle(title); - getPreferenceScreen().addPreference(preferenceCategory); - - addPreferenceScreen(preferenceCategory, R.string.vcard_type, types); - return addPreferenceScreen(preferenceCategory, title, value); - } - - private PreferenceScreen addPreferenceScreen(PreferenceGroup container, - int title, String summary) { - PreferenceScreen preference = getPreferenceManager() - .createPreferenceScreen(this); - preference.setLayoutResource(R.layout.info_preference); - preference.setTitle(title); - preference.setSummary(summary == null ? "" : summary); - container.addPreference(preference); - return preference; - } - - public static Intent createIntent(Context context, String account, - String user) { - return new EntityIntentBuilder(context, ContactViewer.class) - .setAccount(account).setUser(user).build(); - } - - private static String getAccount(Intent intent) { - return AccountIntentBuilder.getAccount(intent); - } - - private static String getUser(Intent intent) { - return EntityIntentBuilder.getUser(intent); - } +import java.util.Collection; +import java.util.List; +public class ContactViewer extends ManagedActivity implements + OnContactChangedListener, OnAccountChangedListener { + + protected ContactTitleExpandableToolbarInflater contactTitleExpandableToolbarInflater; + private String account; + private String bareAddress; + private TextView contactNameView; + + public static Intent createIntent(Context context, String account, String user) { + return new EntityIntentBuilder(context, ContactViewer.class) + .setAccount(account).setUser(user).build(); + } + + private static String getAccount(Intent intent) { + return AccountIntentBuilder.getAccount(intent); + } + + private static String getUser(Intent intent) { + return EntityIntentBuilder.getUser(intent); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + + if (Intent.ACTION_VIEW.equals(getIntent().getAction())) { + // View information about contact from system contact list + Uri data = getIntent().getData(); + if (data != null && "content".equals(data.getScheme())) { + List segments = data.getPathSegments(); + if (segments.size() == 2 && "data".equals(segments.get(0))) { + Long id; + try { + id = Long.valueOf(segments.get(1)); + } catch (NumberFormatException e) { + id = null; + } + if (id != null) + // FIXME: Will be empty while application is loading + for (RosterContact rosterContact : RosterManager.getInstance().getContacts()) + if (id.equals(rosterContact.getViewId())) { + account = rosterContact.getAccount(); + bareAddress = rosterContact.getUser(); + break; + } + } + } + } else { + account = getAccount(getIntent()); + bareAddress = Jid.getBareAddress(getUser(getIntent())); + } + + if (bareAddress != null && bareAddress.equalsIgnoreCase(GroupManager.IS_ACCOUNT)) { + bareAddress = Jid.getBareAddress(AccountManager.getInstance().getAccount(account).getRealJid()); + } + + if (account == null || bareAddress == null) { + Application.getInstance().onError(R.string.ENTRY_IS_NOT_FOUND); + finish(); + return; + } + + if (savedInstanceState == null) { + getFragmentManager().beginTransaction() + .add(R.id.scrollable_container, ContactVcardViewerFragment.newInstance(account, bareAddress)).commit(); + } + + + contactTitleExpandableToolbarInflater = new ContactTitleExpandableToolbarInflater(this); + AbstractContact bestContact = RosterManager.getInstance().getBestContact(account, bareAddress); + contactTitleExpandableToolbarInflater.onCreate(bestContact); + + View contactTitleView = findViewById(R.id.expandable_contact_title); + contactTitleView.findViewById(R.id.status_icon).setVisibility(View.GONE); + contactTitleView.findViewById(R.id.status_text).setVisibility(View.GONE); + contactNameView = (TextView) contactTitleView.findViewById(R.id.name); + + Toolbar toolbar = contactTitleExpandableToolbarInflater.getToolbar(); + toolbar.setNavigationIcon(R.drawable.ic_arrow_left_white_24dp); + toolbar.setNavigationOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + NavUtils.navigateUpFromSameTask(ContactViewer.this); + } + }); + } + + @Override + protected void onResume() { + super.onResume(); + Application.getInstance().addUIListener(OnContactChangedListener.class, this); + Application.getInstance().addUIListener(OnAccountChangedListener.class, this); + + contactTitleExpandableToolbarInflater.onResume(); + } + + @Override + public void onPause() { + super.onPause(); + Application.getInstance().removeUIListener(OnContactChangedListener.class, this); + Application.getInstance().removeUIListener(OnAccountChangedListener.class, this); + } + + @Override + public void onContactsChanged(Collection entities) { + for (BaseEntity entity : entities) { + if (entity.equals(account, bareAddress)) { + contactNameView.setText(RosterManager.getInstance().getBestContact(account, bareAddress).getName()); + break; + } + } + } + + @Override + public void onAccountsChanged(Collection accounts) { + if (accounts.contains(account)) { + contactNameView.setText(RosterManager.getInstance().getBestContact(account, bareAddress).getName()); + } + } + + protected String getAccount() { + return account; + } + + protected String getBareAddress() { + return bareAddress; + } } diff --git a/app/src/main/java/com/xabber/android/ui/FingerprintViewer.java b/app/src/main/java/com/xabber/android/ui/FingerprintViewer.java index 1edd7f29d9..0cf41ff191 100644 --- a/app/src/main/java/com/xabber/android/ui/FingerprintViewer.java +++ b/app/src/main/java/com/xabber/android/ui/FingerprintViewer.java @@ -1,28 +1,24 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.ui; -import java.util.Collection; - import android.app.AlertDialog; -import android.app.Dialog; -import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; -import android.net.Uri; import android.os.Bundle; +import android.support.v7.widget.Toolbar; import android.text.ClipboardManager; import android.view.View; import android.view.View.OnClickListener; @@ -33,6 +29,7 @@ import com.google.zxing.integration.android.IntentIntegrator; import com.google.zxing.integration.android.IntentResult; +import com.xabber.android.R; import com.xabber.android.data.Application; import com.xabber.android.data.account.AccountManager; import com.xabber.android.data.account.OnAccountChangedListener; @@ -44,273 +41,204 @@ import com.xabber.android.data.roster.AbstractContact; import com.xabber.android.data.roster.OnContactChangedListener; import com.xabber.android.data.roster.RosterManager; -import com.xabber.android.ui.dialog.ConfirmDialogBuilder; -import com.xabber.android.ui.dialog.ConfirmDialogListener; -import com.xabber.android.ui.dialog.DialogBuilder; -import com.xabber.android.ui.dialog.NotificationDialogBuilder; -import com.xabber.android.ui.dialog.NotificationDialogListener; -import com.xabber.android.ui.helper.ContactTitleInflater; +import com.xabber.android.ui.helper.ContactTitleActionBarInflater; import com.xabber.android.ui.helper.ManagedActivity; -import com.xabber.androiddev.R; import com.xabber.xmpp.address.Jid; -public class FingerprintViewer extends ManagedActivity implements - OnCheckedChangeListener, OnAccountChangedListener, - OnContactChangedListener, OnClickListener, ConfirmDialogListener, - NotificationDialogListener { - - private static final String SAVED_REMOTE_FINGERPRINT = "com.xabber.android.ui.FingerprintViewer.SAVED_REMOTE_FINGERPRINT"; - private static final String SAVED_LOCAL_FINGERPRINT = "com.xabber.android.ui.FingerprintViewer.SAVED_LOCAL_FINGERPRINT"; - - private String account; - private String user; - private String remoteFingerprint; - private String localFingerprint; - - /** - * UI update is in progress. - */ - private boolean isUpdating; - - private CheckBox verifiedView; - private View scanView; - private View showView; - private View copyView; - - /** - * QR code scanner and generator. - */ - private IntentIntegrator integrator; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (isFinishing()) - return; - - setContentView(R.layout.fingerprint_viewer); - integrator = new IntentIntegrator(this); - Intent intent = getIntent(); - account = getAccount(intent); - user = getUser(intent); - if (AccountManager.getInstance().getAccount(account) == null - || user == null) { - Application.getInstance().onError(R.string.ENTRY_IS_NOT_FOUND); - finish(); - return; - } - if (savedInstanceState != null) { - remoteFingerprint = savedInstanceState - .getString(SAVED_REMOTE_FINGERPRINT); - localFingerprint = savedInstanceState - .getString(SAVED_LOCAL_FINGERPRINT); - } else { - remoteFingerprint = OTRManager.getInstance().getRemoteFingerprint( - account, user); - localFingerprint = OTRManager.getInstance().getLocalFingerprint( - account); - } - verifiedView = (CheckBox) findViewById(R.id.verified); - verifiedView.setOnCheckedChangeListener(this); - scanView = findViewById(R.id.scan); - scanView.setOnClickListener(this); - showView = findViewById(R.id.show); - showView.setOnClickListener(this); - copyView = findViewById(R.id.copy); - copyView.setOnClickListener(this); - isUpdating = false; - } - - @Override - protected void onResume() { - super.onResume(); - Application.getInstance().addUIListener(OnAccountChangedListener.class, - this); - Application.getInstance().addUIListener(OnContactChangedListener.class, - this); - update(); - } - - @Override - protected void onPause() { - super.onPause(); - Application.getInstance().removeUIListener( - OnAccountChangedListener.class, this); - Application.getInstance().removeUIListener( - OnContactChangedListener.class, this); - } - - @Override - protected void onRestoreInstanceState(Bundle savedInstanceState) { - isUpdating = true; - super.onRestoreInstanceState(savedInstanceState); - isUpdating = false; - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putString(SAVED_REMOTE_FINGERPRINT, remoteFingerprint); - outState.putString(SAVED_LOCAL_FINGERPRINT, localFingerprint); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - IntentResult scanResult = IntentIntegrator.parseActivityResult( - requestCode, resultCode, data); - if (scanResult != null) { - String code = scanResult.getContents(); - boolean equals = code != null && code.equals(remoteFingerprint); - verifiedView.setChecked(equals); - if (equals) - showDialog(R.string.action_otr_smp_verified); - else - showDialog(R.string.action_otr_smp_unverified); - } - } - - @Override - public void onContactsChanged(Collection entities) { - String thisBareAddress = Jid.getBareAddress(user); - for (BaseEntity entity : entities) - if (entity.equals(account, thisBareAddress)) { - update(); - break; - } - } - - @Override - public void onAccountsChanged(Collection accounts) { - if (accounts.contains(account)) - update(); - } - - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - switch (buttonView.getId()) { - case R.id.verified: - if (!isUpdating) - OTRManager.getInstance().setVerify(account, user, - remoteFingerprint, isChecked); - break; - default: - break; - } - } - - /** - * Show native dialog instead of provided by ZXing. - * - * @param alertDialog - */ - private void wrapInstallDialog(AlertDialog alertDialog) { - if (alertDialog == null) - return; - alertDialog.dismiss(); - showDialog(R.string.zxing_install_message); - } - - @Override - public void onClick(View view) { - switch (view.getId()) { - case R.id.scan: - wrapInstallDialog(integrator - .initiateScan(IntentIntegrator.QR_CODE_TYPES)); - break; - case R.id.show: - wrapInstallDialog(integrator.shareText(localFingerprint)); - break; - case R.id.copy: - ((ClipboardManager) getSystemService(CLIPBOARD_SERVICE)) - .setText(((TextView) findViewById(R.id.otr_local_fingerprint)) - .getText()); - break; - default: - break; - } - } - - @Override - protected Dialog onCreateDialog(int id) { - switch (id) { - case R.string.zxing_install_message: - return new ConfirmDialogBuilder(this, id, this).setMessage( - R.string.zxing_install_message).create(); - case R.string.zxing_install_fail: - return new NotificationDialogBuilder(this, id, this).setMessage( - R.string.zxing_install_fail).create(); - case R.string.action_otr_smp_verified: - return new NotificationDialogBuilder(this, id, this).setMessage( - R.string.action_otr_smp_verified).create(); - case R.string.action_otr_smp_unverified: - return new NotificationDialogBuilder(this, id, this).setMessage( - R.string.action_otr_smp_unverified).create(); - default: - return super.onCreateDialog(id); - } - } - - @Override - public void onAccept(DialogBuilder dialogBuilder) { - switch (dialogBuilder.getDialogId()) { - case R.string.zxing_install_message: - Uri uri = Uri.parse("market://details?id=" - + IntentIntegrator.BS_PACKAGE); - Intent intent = new Intent(Intent.ACTION_VIEW, uri); - try { - startActivity(intent); - } catch (ActivityNotFoundException anfe) { - showDialog(R.string.zxing_install_fail); - break; - } - break; - default: - break; - } - } - - @Override - public void onDecline(DialogBuilder dialogBuilder) { - } - - @Override - public void onCancel(DialogBuilder dialogBuilder) { - } - - private void update() { - isUpdating = true; - AbstractContact abstractContact = RosterManager.getInstance() - .getBestContact(account, user); - ContactTitleInflater.updateTitle(findViewById(R.id.title), this, - abstractContact); - verifiedView.setChecked(OTRManager.getInstance().isVerified(account, - user)); - scanView.setEnabled(remoteFingerprint != null); - verifiedView.setEnabled(remoteFingerprint != null); - ((TextView) findViewById(R.id.otr_remote_fingerprint)) - .setText(remoteFingerprint == null ? getString(R.string.unknown) - : CertificateManager.showFingerprint(remoteFingerprint)); - showView.setEnabled(localFingerprint != null); - copyView.setEnabled(localFingerprint != null); - ((TextView) findViewById(R.id.otr_local_fingerprint)) - .setText(localFingerprint == null ? getString(R.string.unknown) - : CertificateManager.showFingerprint(localFingerprint)); - isUpdating = false; - } - - public static Intent createIntent(Context context, String account, - String user) { - return new EntityIntentBuilder(context, FingerprintViewer.class) - .setAccount(account).setUser(user).build(); - } - - private static String getAccount(Intent intent) { - return AccountIntentBuilder.getAccount(intent); - } +import java.util.Collection; - private static String getUser(Intent intent) { - return EntityIntentBuilder.getUser(intent); - } +public class FingerprintViewer extends ManagedActivity implements + OnCheckedChangeListener, OnAccountChangedListener, + OnContactChangedListener, OnClickListener { + + private static final String SAVED_REMOTE_FINGERPRINT = "com.xabber.android.ui.FingerprintViewer.SAVED_REMOTE_FINGERPRINT"; + private static final String SAVED_LOCAL_FINGERPRINT = "com.xabber.android.ui.FingerprintViewer.SAVED_LOCAL_FINGERPRINT"; + ContactTitleActionBarInflater contactTitleActionBarInflater; + private String account; + private String user; + private String remoteFingerprint; + private String localFingerprint; + /** + * UI update is in progress. + */ + private boolean isUpdating; + private CheckBox verifiedView; + private View scanView; + private View showView; + private View copyView; + /** + * QR code scanner and generator. + */ + private IntentIntegrator integrator; + + public static Intent createIntent(Context context, String account, String user) { + return new EntityIntentBuilder(context, FingerprintViewer.class) + .setAccount(account).setUser(user).build(); + } + + private static String getAccount(Intent intent) { + return AccountIntentBuilder.getAccount(intent); + } + + private static String getUser(Intent intent) { + return EntityIntentBuilder.getUser(intent); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (isFinishing()) { + return; + } + + setContentView(R.layout.fingerprint_viewer); + + integrator = new IntentIntegrator(this); + Intent intent = getIntent(); + account = getAccount(intent); + user = getUser(intent); + if (AccountManager.getInstance().getAccount(account) == null || user == null) { + Application.getInstance().onError(R.string.ENTRY_IS_NOT_FOUND); + finish(); + return; + } + if (savedInstanceState != null) { + remoteFingerprint = savedInstanceState.getString(SAVED_REMOTE_FINGERPRINT); + localFingerprint = savedInstanceState.getString(SAVED_LOCAL_FINGERPRINT); + } else { + remoteFingerprint = OTRManager.getInstance().getRemoteFingerprint(account, user); + localFingerprint = OTRManager.getInstance().getLocalFingerprint(account); + } + verifiedView = (CheckBox) findViewById(R.id.verified); + verifiedView.setOnCheckedChangeListener(this); + scanView = findViewById(R.id.scan); + scanView.setOnClickListener(this); + showView = findViewById(R.id.show); + showView.setOnClickListener(this); + copyView = findViewById(R.id.copy); + copyView.setOnClickListener(this); + isUpdating = false; + + contactTitleActionBarInflater = new ContactTitleActionBarInflater(this, (Toolbar) findViewById(R.id.toolbar_default)); + contactTitleActionBarInflater.setUpActionBarView(); + } + + @Override + protected void onResume() { + super.onResume(); + Application.getInstance().addUIListener(OnAccountChangedListener.class, this); + Application.getInstance().addUIListener(OnContactChangedListener.class, this); + update(); + } + + @Override + protected void onPause() { + super.onPause(); + Application.getInstance().removeUIListener(OnAccountChangedListener.class, this); + Application.getInstance().removeUIListener(OnContactChangedListener.class, this); + } + + @Override + protected void onRestoreInstanceState(Bundle savedInstanceState) { + isUpdating = true; + super.onRestoreInstanceState(savedInstanceState); + isUpdating = false; + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putString(SAVED_REMOTE_FINGERPRINT, remoteFingerprint); + outState.putString(SAVED_LOCAL_FINGERPRINT, localFingerprint); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + IntentResult scanResult = IntentIntegrator.parseActivityResult( + requestCode, resultCode, data); + if (scanResult != null) { + String code = scanResult.getContents(); + boolean equals = code != null && code.equals(remoteFingerprint); + verifiedView.setChecked(equals); + + int dialogMessageId; + if (equals) { + dialogMessageId = R.string.action_otr_smp_verified; + } else { + dialogMessageId = R.string.action_otr_smp_unverified; + } + new AlertDialog.Builder(this).setMessage(dialogMessageId) + .setNeutralButton(android.R.string.ok, null).show(); + } + } + + @Override + public void onContactsChanged(Collection entities) { + String thisBareAddress = Jid.getBareAddress(user); + for (BaseEntity entity : entities) { + if (entity.equals(account, thisBareAddress)) { + update(); + break; + } + } + } + + @Override + public void onAccountsChanged(Collection accounts) { + if (accounts.contains(account)) { + update(); + } + } + + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + switch (buttonView.getId()) { + case R.id.verified: + if (!isUpdating) { + OTRManager.getInstance().setVerify(account, user, remoteFingerprint, isChecked); + } + break; + default: + break; + } + } + + @Override + public void onClick(View view) { + switch (view.getId()) { + case R.id.scan: + integrator.initiateScan(IntentIntegrator.QR_CODE_TYPES); + break; + case R.id.show: + integrator.shareText(localFingerprint); + break; + case R.id.copy: + ((ClipboardManager) getSystemService(CLIPBOARD_SERVICE)) + .setText(((TextView) findViewById(R.id.otr_local_fingerprint)).getText()); + break; + default: + break; + } + } + + private void update() { + isUpdating = true; + AbstractContact abstractContact = RosterManager.getInstance().getBestContact(account, user); + + verifiedView.setChecked(OTRManager.getInstance().isVerified(account, user)); + scanView.setEnabled(remoteFingerprint != null); + verifiedView.setEnabled(remoteFingerprint != null); + ((TextView) findViewById(R.id.otr_remote_fingerprint)).setText( + remoteFingerprint == null ? getString(R.string.unknown) : CertificateManager.showFingerprint(remoteFingerprint)); + showView.setEnabled(localFingerprint != null); + copyView.setEnabled(localFingerprint != null); + ((TextView) findViewById(R.id.otr_local_fingerprint)).setText( + localFingerprint == null ? getString(R.string.unknown) : CertificateManager.showFingerprint(localFingerprint)); + + contactTitleActionBarInflater.update(abstractContact); + + isUpdating = false; + } } diff --git a/app/src/main/java/com/xabber/android/ui/GroupEditor.java b/app/src/main/java/com/xabber/android/ui/GroupEditor.java new file mode 100644 index 0000000000..44db52f078 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/GroupEditor.java @@ -0,0 +1,123 @@ +/** + * Copyright (c) 2013, Redsolution LTD. All rights reserved. + * + * This file is part of Xabber project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License, Version 3. + * + * Xabber is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License, + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.xabber.android.ui; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.widget.Toolbar; + +import com.xabber.android.R; +import com.xabber.android.data.Application; +import com.xabber.android.data.account.AccountManager; +import com.xabber.android.data.account.OnAccountChangedListener; +import com.xabber.android.data.entity.BaseEntity; +import com.xabber.android.data.intent.EntityIntentBuilder; +import com.xabber.android.data.roster.AbstractContact; +import com.xabber.android.data.roster.OnContactChangedListener; +import com.xabber.android.data.roster.RosterManager; +import com.xabber.android.ui.helper.ContactTitleActionBarInflater; +import com.xabber.android.ui.helper.ManagedActivity; +import com.xabber.xmpp.address.Jid; + +import java.util.Collection; + +public class GroupEditor extends ManagedActivity implements OnContactChangedListener, + OnAccountChangedListener { + + ContactTitleActionBarInflater contactTitleActionBarInflater; + private String account; + private String user; + + public static Intent createIntent(Context context, String account, String user) { + Intent intent = new EntityIntentBuilder(context, GroupEditor.class) + .setAccount(account).setUser(user).build(); + intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); + return intent; + } + + private static String getAccount(Intent intent) { + return EntityIntentBuilder.getAccount(intent); + } + + private static String getUser(Intent intent) { + return EntityIntentBuilder.getUser(intent); + } + + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_with_toolbar_and_container); + + contactTitleActionBarInflater = new ContactTitleActionBarInflater(this, (Toolbar) findViewById(R.id.toolbar_default)); + contactTitleActionBarInflater.setUpActionBarView(); + + Intent intent = getIntent(); + account = GroupEditor.getAccount(intent); + user = GroupEditor.getUser(intent); + + if (AccountManager.getInstance().getAccount(account) == null || user == null) { + Application.getInstance().onError(R.string.ENTRY_IS_NOT_FOUND); + finish(); + } + + if (savedInstanceState == null) { + getSupportFragmentManager().beginTransaction() + .add(R.id.fragment_container, GroupEditorFragment.newInstance(account, user)).commit(); + } + + } + + @Override + protected void onResume() { + super.onResume(); + Application.getInstance().addUIListener(OnAccountChangedListener.class, this); + Application.getInstance().addUIListener(OnContactChangedListener.class, this); + update(); + } + + @Override + protected void onPause() { + super.onPause(); + Application.getInstance().removeUIListener(OnAccountChangedListener.class, this); + Application.getInstance().removeUIListener(OnContactChangedListener.class, this); + } + + private void update() { + AbstractContact abstractContact = RosterManager.getInstance().getBestContact(account, user); + contactTitleActionBarInflater.update(abstractContact); + contactTitleActionBarInflater.setStatusText(user); + contactTitleActionBarInflater.hideStatusIcon(); + } + + @Override + public void onContactsChanged(Collection entities) { + String thisBareAddress = Jid.getBareAddress(user); + for (BaseEntity entity : entities) { + if (entity.equals(account, thisBareAddress)) { + update(); + break; + } + } + } + + @Override + public void onAccountsChanged(Collection accounts) { + if (accounts.contains(account)) { + update(); + } + } + +} diff --git a/app/src/main/java/com/xabber/android/ui/GroupEditorFragment.java b/app/src/main/java/com/xabber/android/ui/GroupEditorFragment.java new file mode 100644 index 0000000000..cb9ff12388 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/GroupEditorFragment.java @@ -0,0 +1,257 @@ +package com.xabber.android.ui; + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.support.v4.app.ListFragment; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.LayoutInflater; +import android.view.View; +import android.view.inputmethod.InputMethodManager; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.ListView; + +import com.xabber.android.R; +import com.xabber.android.data.Application; +import com.xabber.android.data.NetworkException; +import com.xabber.android.data.roster.RosterManager; +import com.xabber.android.ui.adapter.GroupEditorAdapter; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; + +public class GroupEditorFragment extends ListFragment implements TextWatcher, View.OnClickListener { + + protected static final String ARG_ACCOUNT = "com.xabber.android.ui.GroupEditorFragment.ARG_ACCOUNT"; + protected static final String ARG_USER = "com.xabber.android.ui.GroupEditorFragment.ARG_USER"; + + private static final String SAVED_GROUPS = "com.xabber.android.ui.GroupEditorFragment.SAVED_GROUPS"; + private static final String SAVED_SELECTED = "com.xabber.android.ui.GroupEditorFragment.SAVED_SELECTED"; + private static final String SAVED_ADD_GROUP_NAME = "com.xabber.android.ui.GroupEditorFragment.SAVED_ADD_GROUP_NAME"; + + private String account; + private String user; + + private GroupEditorAdapter groupEditorAdapter; + + private Collection groups; + private Collection selected = new HashSet<>(); + + private EditText groupAddInput; + private CheckBox groupAddCheckBox; + private View footerView; + + /** + * Mandatory empty constructor for the fragment manager to instantiate the + * fragment (e.g. upon screen orientation changes). + */ + public GroupEditorFragment() { + } + + public static GroupEditorFragment newInstance(String account, String user) { + GroupEditorFragment fragment = new GroupEditorFragment(); + Bundle args = new Bundle(); + args.putString(ARG_ACCOUNT, account); + args.putString(ARG_USER, user); + fragment.setArguments(args); + return fragment; + } + + public static void hideKeyboard(Activity activity) { + // Check if no view has focus: + View view = activity.getCurrentFocus(); + if (view != null) { + InputMethodManager inputManager = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); + inputManager.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); + } + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (getArguments() != null) { + account = getArguments().getString(ARG_ACCOUNT); + user = getArguments().getString(ARG_USER); + } + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + + setUpFooter(); + + groupEditorAdapter = new GroupEditorAdapter(getActivity(), + R.layout.group_list_item, new ArrayList()); + + setListAdapter(groupEditorAdapter); + + if (savedInstanceState != null) { + groups = savedInstanceState.getStringArrayList(SAVED_GROUPS); + selected = savedInstanceState.getStringArrayList(SAVED_SELECTED); + groupAddInput.setText(savedInstanceState.getString(SAVED_ADD_GROUP_NAME)); + } else { + setAccountGroups(); + if (user != null) { + selected = RosterManager.getInstance().getGroups(account, user); + } + } + + } + + protected void setAccountGroups() { + groups = RosterManager.getInstance().getGroups(account); + } + + private void setUpFooter() { + footerView = ((LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.group_add_footer, null, false); + getListView().addFooterView(footerView); + + groupAddInput = (EditText) footerView.findViewById(R.id.group_add_input); + groupAddInput.addTextChangedListener(this); + + groupAddCheckBox = (CheckBox) footerView.findViewById(R.id.group_add_checkbox); + groupAddCheckBox.setVisibility(View.INVISIBLE); + groupAddCheckBox.setOnClickListener(this); + } + + @Override + public void onResume() { + super.onResume(); + + updateGroups(); + } + + protected void updateGroups() { + ArrayList list = new ArrayList<>(groups); + Collections.sort(list); + groupEditorAdapter.clear(); + + for (int position = 0; position < list.size(); position++) { + String groupName = list.get(position); + + GroupEditorAdapter.Group group = new GroupEditorAdapter.Group(groupName, selected.contains(groupName)); + + groupEditorAdapter.add(group); + } + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + + + selected = getSelected(); + + outState.putStringArrayList(SAVED_GROUPS, getGroups()); + outState.putStringArrayList(SAVED_SELECTED, new ArrayList<>(selected)); + outState.putString(SAVED_ADD_GROUP_NAME, groupAddInput.getText().toString()); + } + + @Override + public void onPause() { + super.onPause(); + + selected = getSelected(); + + if (account != null && !"".equals(user)) { + try { + RosterManager.getInstance().setGroups(account, user, selected); + } catch (NetworkException e) { + Application.getInstance().onError(e); + } + } + + } + + @Override + public void onListItemClick(ListView l, View v, int position, long id) { + super.onListItemClick(l, v, position, id); + + CheckBox checkBox = (CheckBox) v.findViewById(R.id.group_item_selected_checkbox); + checkBox.toggle(); + + GroupEditorAdapter.Group group = groupEditorAdapter.getItem(position - getListView().getHeaderViewsCount()); + + group.setIsSelected(checkBox.isChecked()); + } + + protected ArrayList getGroups() { + ArrayList groups = new ArrayList<>(); + for (int position = 0; position < groupEditorAdapter.getCount(); position++) + groups.add(groupEditorAdapter.getItem(position).getGroupName()); + return groups; + } + + public ArrayList getSelected() { + ArrayList selectedGroups = new ArrayList<>(); + for (int position = 0; position < groupEditorAdapter.getCount(); position++) { + + GroupEditorAdapter.Group item = groupEditorAdapter.getItem(position); + + if (item.isSelected()) { + selectedGroups.add(item.getGroupName()); + } + } + return selectedGroups; + } + + @Override + public void afterTextChanged(Editable s) { + String groupName = groupAddInput.getText().toString().trim(); + if (groupName.isEmpty() || getGroups().contains(groupName)) { + groupAddCheckBox.setVisibility(View.INVISIBLE); + } else { + groupAddCheckBox.setVisibility(View.VISIBLE); + } + } + + @Override + public void onClick(View v) { + if (v.getId() == R.id.group_add_checkbox) { + String groupName = groupAddInput.getText().toString().trim(); + groupEditorAdapter.add(new GroupEditorAdapter.Group(groupName, true)); + + groupAddInput.getText().clear(); + groupAddInput.clearFocus(); + hideKeyboard(getActivity()); + groupAddCheckBox.setChecked(false); + } + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + + } + + protected String getAccount() { + return account; + } + + protected void setAccount(String account) { + this.account = account; + } + + protected String getUser() { + return user; + } + + protected void setUser(String user) { + this.user = user; + } + + protected View getFooterView() { + return footerView; + } +} diff --git a/app/src/main/java/com/xabber/android/ui/GroupListActivity.java b/app/src/main/java/com/xabber/android/ui/GroupListActivity.java deleted file mode 100644 index c89d7c9ad4..0000000000 --- a/app/src/main/java/com/xabber/android/ui/GroupListActivity.java +++ /dev/null @@ -1,215 +0,0 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.xabber.android.ui; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; - -import android.app.Dialog; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.ArrayAdapter; -import android.widget.ListView; -import android.widget.TextView; - -import com.xabber.android.ui.dialog.ConfirmDialogListener; -import com.xabber.android.ui.dialog.DialogBuilder; -import com.xabber.android.ui.dialog.GroupAddDialogBuilder; -import com.xabber.android.ui.helper.ManagedListActivity; -import com.xabber.androiddev.R; - -/** - * Manage list of selected groups. - * - * @author alexander.ivanov - * - */ -public abstract class GroupListActivity extends ManagedListActivity implements - ConfirmDialogListener, OnItemClickListener { - - private static final String SAVED_GROUPS = "com.xabber.android.ui.ContactList.SAVED_GROUPS"; - private static final String SAVED_SELECTED = "com.xabber.android.ui.ContactList.SAVED_SELECTED"; - - static final int OPTION_MENU_ADD_GROUP_ID = 1; - - static final int DIALOG_ADD_GROUP_ID = 0x10; - - private ArrayAdapter arrayAdapter; - - private ListView listView; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (isFinishing()) - return; - onInflate(savedInstanceState); - if (isFinishing()) - return; - Collection groups; - Collection selected; - if (savedInstanceState != null) { - groups = savedInstanceState.getStringArrayList(SAVED_GROUPS); - selected = savedInstanceState.getStringArrayList(SAVED_SELECTED); - } else { - groups = getInitialGroups(); - selected = getInitialSelected(); - } - - listView = getListView(); - listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); - listView.setOnItemClickListener(this); - arrayAdapter = new ArrayAdapter(this, - android.R.layout.simple_list_item_multiple_choice, - new ArrayList()); - LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE); - View view = inflater.inflate(R.layout.add_item, listView, false); - ((TextView) view.findViewById(android.R.id.message)) - .setText(R.string.group_add); - listView.addFooterView(view, null, true); - setListAdapter(arrayAdapter); - setGroups(groups, selected); - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putStringArrayList(SAVED_GROUPS, getGroups()); - outState.putStringArrayList(SAVED_SELECTED, getSelected()); - } - - /** - * Inflates layout. - * - * @param savedInstanceState - */ - protected abstract void onInflate(Bundle savedInstanceState); - - /** - * @return List of initial allowed groups. - */ - abstract Collection getInitialGroups(); - - /** - * @return List of initially selected groups. - */ - abstract Collection getInitialSelected(); - - /** - * @return Actual groups from adapter. - */ - private ArrayList getGroups() { - ArrayList groups = new ArrayList(); - for (int position = 0; position < arrayAdapter.getCount(); position++) - groups.add(arrayAdapter.getItem(position)); - return groups; - } - - /** - * @return Actual selected groups from adapter. - */ - public ArrayList getSelected() { - ArrayList groups = new ArrayList(); - for (int position = 0; position < arrayAdapter.getCount(); position++) - if (listView.isItemChecked(position - + listView.getHeaderViewsCount())) { - groups.add(arrayAdapter.getItem(position)); - } - return groups; - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - menu.add(0, OPTION_MENU_ADD_GROUP_ID, 0, - getResources().getText(R.string.group_add)).setIcon( - android.R.drawable.ic_menu_add); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case OPTION_MENU_ADD_GROUP_ID: - showDialog(DIALOG_ADD_GROUP_ID); - return true; - } - return false; - } - - @Override - protected Dialog onCreateDialog(int id) { - super.onCreateDialog(id); - switch (id) { - case DIALOG_ADD_GROUP_ID: - return new GroupAddDialogBuilder(this, DIALOG_ADD_GROUP_ID, this, - getGroups()).create(); - default: - return null; - } - } - - @Override - public void onItemClick(AdapterView parent, View view, int position, - long id) { - if (listView.getItemAtPosition(position) == null) // Footer - showDialog(DIALOG_ADD_GROUP_ID); - } - - /** - * Sets new list of groups and select specified groups. - * - * @param groups - * @param selected - */ - void setGroups(Collection groups, Collection selected) { - ArrayList list = new ArrayList(groups); - Collections.sort(list); - arrayAdapter.clear(); - for (int position = 0; position < list.size(); position++) { - String group = list.get(position); - arrayAdapter.add(group); - listView.setItemChecked(position + listView.getHeaderViewsCount(), - selected.contains(group)); - } - } - - @Override - public void onAccept(DialogBuilder dialog) { - switch (dialog.getDialogId()) { - case DIALOG_ADD_GROUP_ID: - String group = ((GroupAddDialogBuilder) dialog).getName(); - ArrayList groups = getGroups(); - groups.add(group); - ArrayList selected = getSelected(); - selected.add(group); - setGroups(groups, selected); - } - } - - @Override - public void onDecline(DialogBuilder dialog) { - } - - @Override - public void onCancel(DialogBuilder dialog) { - } - -} diff --git a/app/src/main/java/com/xabber/android/ui/LoadActivity.java b/app/src/main/java/com/xabber/android/ui/LoadActivity.java index 7eab51b3e5..694f97b68c 100644 --- a/app/src/main/java/com/xabber/android/ui/LoadActivity.java +++ b/app/src/main/java/com/xabber/android/ui/LoadActivity.java @@ -1,21 +1,19 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.ui; -import java.util.Collection; - import android.content.Context; import android.content.Intent; import android.os.Bundle; @@ -25,81 +23,83 @@ import android.view.animation.AnimationUtils; import android.widget.TextView; +import com.xabber.android.R; import com.xabber.android.data.ActivityManager; import com.xabber.android.data.Application; import com.xabber.android.data.LogManager; import com.xabber.android.data.account.OnAccountChangedListener; import com.xabber.android.service.XabberService; import com.xabber.android.ui.helper.SingleActivity; -import com.xabber.androiddev.R; + +import java.util.Collection; public class LoadActivity extends SingleActivity implements - OnAccountChangedListener { + OnAccountChangedListener { - private Animation animation; - private View disconnectedView; + private Animation animation; + private View disconnectedView; - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.load); - animation = AnimationUtils.loadAnimation(this, R.anim.connection); - disconnectedView = findViewById(R.id.disconnected); - } + public static Intent createIntent(Context context) { + return new Intent(context, LoadActivity.class); + } - @Override - protected void onResume() { - super.onResume(); - Application.getInstance().addUIListener(OnAccountChangedListener.class, - this); - if (Application.getInstance().isClosing()) { - ((TextView) findViewById(R.id.text)) - .setText(R.string.application_state_closing); - } else { - startService(XabberService.createIntent(this)); - disconnectedView.startAnimation(animation); - update(); - } - } + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.load); + animation = AnimationUtils.loadAnimation(this, R.anim.connection); + disconnectedView = findViewById(R.id.disconnected); + } - @Override - protected void onPause() { - super.onPause(); - Application.getInstance().removeUIListener( - OnAccountChangedListener.class, this); - disconnectedView.clearAnimation(); - } + @Override + protected void onResume() { + super.onResume(); + Application.getInstance().addUIListener(OnAccountChangedListener.class, + this); + if (Application.getInstance().isClosing()) { + ((TextView) findViewById(R.id.text)) + .setText(R.string.application_state_closing); + } else { + startService(XabberService.createIntent(this)); + disconnectedView.startAnimation(animation); + update(); + } + } - @Override - public void onAccountsChanged(Collection accounts) { - update(); - } + @Override + protected void onPause() { + super.onPause(); + Application.getInstance().removeUIListener( + OnAccountChangedListener.class, this); + disconnectedView.clearAnimation(); + } - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - switch (keyCode) { - case KeyEvent.KEYCODE_BACK: - cancel(); - break; - } - return super.onKeyDown(keyCode, event); - } + @Override + public void onAccountsChanged(Collection accounts) { + update(); + } - private void update() { - if (Application.getInstance().isInitialized() - && !Application.getInstance().isClosing() && !isFinishing()) { - LogManager.i(this, "Initialized"); - finish(); - } - } + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + switch (keyCode) { + case KeyEvent.KEYCODE_BACK: + cancel(); + break; + } + return super.onKeyDown(keyCode, event); + } - private void cancel() { - finish(); - ActivityManager.getInstance().cancelTask(this); - } + private void update() { + if (Application.getInstance().isInitialized() + && !Application.getInstance().isClosing() && !isFinishing()) { + LogManager.i(this, "Initialized"); + finish(); + } + } - public static Intent createIntent(Context context) { - return new Intent(context, LoadActivity.class); - } + private void cancel() { + finish(); + ActivityManager.getInstance().cancelTask(this); + } } diff --git a/app/src/main/java/com/xabber/android/ui/MUCEditor.java b/app/src/main/java/com/xabber/android/ui/MUCEditor.java deleted file mode 100644 index 4c69378319..0000000000 --- a/app/src/main/java/com/xabber/android/ui/MUCEditor.java +++ /dev/null @@ -1,319 +0,0 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.xabber.android.ui; - -import java.util.Collection; - -import org.jivesoftware.smack.util.StringUtils; - -import android.app.Dialog; -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.view.View; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemSelectedListener; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.EditText; -import android.widget.Spinner; -import android.widget.Toast; - -import com.xabber.android.data.Application; -import com.xabber.android.data.account.AccountManager; -import com.xabber.android.data.extension.muc.MUCManager; -import com.xabber.android.data.extension.muc.RoomInvite; -import com.xabber.android.data.intent.AccountIntentBuilder; -import com.xabber.android.data.intent.EntityIntentBuilder; -import com.xabber.android.data.message.MessageManager; -import com.xabber.android.data.notification.NotificationManager; -import com.xabber.android.ui.adapter.AccountChooseAdapter; -import com.xabber.android.ui.dialog.ConfirmDialogBuilder; -import com.xabber.android.ui.dialog.ConfirmDialogListener; -import com.xabber.android.ui.dialog.DialogBuilder; -import com.xabber.android.ui.helper.ManagedActivity; -import com.xabber.androiddev.R; - -public class MUCEditor extends ManagedActivity implements View.OnClickListener, - OnItemSelectedListener, ConfirmDialogListener { - - /** - * Action for MUC invitation to be show. - * - * Clear action on dialog dismiss. - */ - private static final String ACTION_MUC_INVITE = "com.xabber.android.data.MUC_INVITE"; - - private static final String SAVED_ACCOUNT = "com.xabber.android.ui.MUCEditor.SAVED_ACCOUNT"; - private static final String SAVED_ROOM = "com.xabber.android.ui.MUCEditor.SAVED_ROOM"; - - private static final int DIALOG_MUC_INVITE_ID = 100; - - private String account; - private String room; - - /** - * Last selected account. - */ - private int selectedAccount; - - /** - * Invite intent. - */ - private RoomInvite roomInvite; - - /** - * Views. - */ - private Spinner accountView; - private EditText serverView; - private EditText roomView; - private EditText nickView; - private EditText passwordView; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (isFinishing()) - return; - - setContentView(R.layout.muc_editor); - - accountView = (Spinner) findViewById(R.id.contact_account); - serverView = (EditText) findViewById(R.id.muc_server); - roomView = (EditText) findViewById(R.id.muc_room); - nickView = (EditText) findViewById(R.id.muc_nick); - passwordView = (EditText) findViewById(R.id.muc_password); - - ((Button) findViewById(R.id.ok)).setOnClickListener(this); - accountView.setAdapter(new AccountChooseAdapter(this)); - accountView.setOnItemSelectedListener(this); - - Intent intent = getIntent(); - if (savedInstanceState != null) { - account = savedInstanceState.getString(SAVED_ACCOUNT); - room = savedInstanceState.getString(SAVED_ROOM); - } else { - account = getAccount(intent); - room = getUser(intent); - if (room != null) { - serverView.setText(StringUtils.parseServer(room)); - roomView.setText(StringUtils.parseName(room)); - } - if (account != null && room != null) { - MUCManager.getInstance() - .removeAuthorizationError(account, room); - nickView.setText(MUCManager.getInstance().getNickname(account, - room)); - passwordView.setText(MUCManager.getInstance().getPassword( - account, room)); - } - } - if (account == null) { - Collection accounts = AccountManager.getInstance() - .getAccounts(); - if (accounts.size() == 1) - account = accounts.iterator().next(); - } - if (account != null) { - for (int position = 0; position < accountView.getCount(); position++) - if (account.equals(accountView.getItemAtPosition(position))) { - accountView.setSelection(position); - break; - } - } - if ("".equals(nickView.getText().toString())) - nickView.setText(getNickname(((String) accountView - .getSelectedItem()))); - if (ACTION_MUC_INVITE.equals(intent.getAction())) { - roomInvite = MUCManager.getInstance().getInvite(account, room); - if (roomInvite == null) { - Application.getInstance().onError(R.string.ENTRY_IS_NOT_FOUND); - finish(); - return; - } - passwordView.setText(roomInvite.getPassword()); - } else { - roomInvite = null; - } - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - account = (String) accountView.getSelectedItem(); - outState.putString(SAVED_ACCOUNT, account); - outState.putString(SAVED_ROOM, room); - } - - @Override - protected void onResume() { - super.onResume(); - selectedAccount = accountView.getSelectedItemPosition(); - if (roomInvite != null) - showDialog(DIALOG_MUC_INVITE_ID); - } - - @Override - public void onClick(View view) { - switch (view.getId()) { - case R.id.ok: - String account = (String) accountView.getSelectedItem(); - if (account == null) { - Toast.makeText(this, getString(R.string.EMPTY_ACCOUNT), - Toast.LENGTH_LONG).show(); - return; - } - String server = serverView.getText().toString(); - if ("".equals(server)) { - Toast.makeText(this, getString(R.string.EMPTY_SERVER_NAME), - Toast.LENGTH_LONG).show(); - return; - } - String room = roomView.getText().toString(); - if ("".equals(room)) { - Toast.makeText(this, getString(R.string.EMPTY_ROOM_NAME), - Toast.LENGTH_LONG).show(); - return; - } - String nick = nickView.getText().toString(); - if ("".equals(nick)) { - Toast.makeText(this, getString(R.string.EMPTY_NICK_NAME), - Toast.LENGTH_LONG).show(); - return; - } - String password = passwordView.getText().toString(); - boolean join = ((CheckBox) findViewById(R.id.muc_join)).isChecked(); - room = room + "@" + server; - if (this.account != null && this.room != null) - if (!account.equals(this.account) || !room.equals(this.room)) { - MUCManager.getInstance().removeRoom(this.account, this.room); - MessageManager.getInstance().closeChat(this.account, - this.room); - NotificationManager.getInstance() - .removeMessageNotification(this.account, this.room); - } - MUCManager.getInstance() - .createRoom(account, room, nick, password, join); - finish(); - break; - default: - break; - } - } - - /** - * @param account - * @return Suggested nickname in the room. - */ - private String getNickname(String account) { - if (account == null) - return ""; - String nickname = AccountManager.getInstance().getNickName(account); - String name = StringUtils.parseName(nickname); - if ("".equals(name)) - return nickname; - else - return name; - } - - @Override - protected Dialog onCreateDialog(int id) { - super.onCreateDialog(id); - switch (id) { - case DIALOG_MUC_INVITE_ID: - return new ConfirmDialogBuilder(this, DIALOG_MUC_INVITE_ID, this) - .setMessage(roomInvite.getConfirmation()).create(); - default: - return null; - } - } - - @Override - public void onItemSelected(AdapterView parent, View view, int position, - long id) { - String current = nickView.getText().toString(); - String previous; - if (selectedAccount == AdapterView.INVALID_POSITION) - previous = ""; - else - previous = getNickname((String) accountView.getAdapter().getItem( - selectedAccount)); - if (current.equals(previous)) - nickView.setText(getNickname((String) accountView.getSelectedItem())); - selectedAccount = accountView.getSelectedItemPosition(); - } - - @Override - public void onNothingSelected(AdapterView parent) { - selectedAccount = accountView.getSelectedItemPosition(); - } - - @Override - public void onAccept(DialogBuilder dialogBuilder) { - switch (dialogBuilder.getDialogId()) { - case DIALOG_MUC_INVITE_ID: - MUCManager.getInstance().removeInvite(roomInvite); - getIntent().setAction(null); - account = null; - room = null; - break; - } - } - - @Override - public void onDecline(DialogBuilder dialogBuilder) { - switch (dialogBuilder.getDialogId()) { - case DIALOG_MUC_INVITE_ID: - MUCManager.getInstance().removeInvite(roomInvite); - finish(); - break; - } - } - - @Override - public void onCancel(DialogBuilder dialogBuilder) { - switch (dialogBuilder.getDialogId()) { - case DIALOG_MUC_INVITE_ID: - finish(); - break; - } - } - - public static Intent createIntent(Context context) { - return MUCEditor.createIntent(context, null, null); - } - - public static Intent createIntent(Context context, String account, - String room) { - return new EntityIntentBuilder(context, MUCEditor.class) - .setAccount(account).setUser(room).build(); - } - - public static Intent createInviteIntent(Context context, String account, - String user) { - Intent intent = createIntent(context, account, user); - intent.setAction(ACTION_MUC_INVITE); - return intent; - } - - private static String getAccount(Intent intent) { - return AccountIntentBuilder.getAccount(intent); - } - - private static String getUser(Intent intent) { - return EntityIntentBuilder.getUser(intent); - } -} diff --git a/app/src/main/java/com/xabber/android/ui/MUCInvite.java b/app/src/main/java/com/xabber/android/ui/MUCInvite.java new file mode 100644 index 0000000000..3429d14b35 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/MUCInvite.java @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2013, Redsolution LTD. All rights reserved. + * + * This file is part of Xabber project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License, Version 3. + * + * Xabber is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License, + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.xabber.android.ui; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.view.View; + +import com.xabber.android.R; +import com.xabber.android.data.Application; +import com.xabber.android.data.extension.muc.MUCManager; +import com.xabber.android.data.extension.muc.RoomInvite; +import com.xabber.android.data.intent.AccountIntentBuilder; +import com.xabber.android.data.intent.EntityIntentBuilder; +import com.xabber.android.ui.helper.ManagedDialog; + +public class MUCInvite extends ManagedDialog { + + private String account; + private String room; + private RoomInvite roomInvite; + + public static Intent createIntent(Context context, String account, + String room) { + return new EntityIntentBuilder(context, MUCInvite.class) + .setAccount(account).setUser(room).build(); + } + + private static String getAccount(Intent intent) { + return AccountIntentBuilder.getAccount(intent); + } + + private static String getUser(Intent intent) { + return EntityIntentBuilder.getUser(intent); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Intent intent = getIntent(); + account = getAccount(intent); + room = getUser(intent); + roomInvite = MUCManager.getInstance().getInvite(account, room); + if (roomInvite == null) { + Application.getInstance().onError(R.string.ENTRY_IS_NOT_FOUND); + finish(); + return; + } + setDialogMessage(roomInvite.getConfirmation()); + setDialogTitle(R.string.subscription_request_message); + findViewById(android.R.id.button3).setVisibility(View.GONE); + } + + @Override + public void onAccept() { + super.onAccept(); + startActivity(ConferenceAdd.createIntent(this, account, room)); + finish(); + } + + @Override + public void onDecline() { + super.onDecline(); + MUCManager.getInstance().removeInvite(roomInvite); + finish(); + } + +} diff --git a/app/src/main/java/com/xabber/android/ui/OAuthActivity.java b/app/src/main/java/com/xabber/android/ui/OAuthActivity.java index cc3c43dfad..c80d0faf76 100644 --- a/app/src/main/java/com/xabber/android/ui/OAuthActivity.java +++ b/app/src/main/java/com/xabber/android/ui/OAuthActivity.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -24,146 +24,145 @@ import android.webkit.WebView; import android.webkit.WebViewClient; +import com.xabber.android.R; import com.xabber.android.data.LogManager; import com.xabber.android.data.NetworkException; import com.xabber.android.data.account.AccountProtocol; import com.xabber.android.data.account.OAuthManager; import com.xabber.android.ui.helper.ManagedActivity; -import com.xabber.androiddev.R; /** * Activity with WebView for OAuth authorization. - * + * * @author alexander.ivanov - * */ public class OAuthActivity extends ManagedActivity { - private static final String EXTRA_INVALIDATE = "com.xabber.android.ui.OAuthVerifier.EXTRA_CANCELED"; - private static final String EXTRA_REFRESH_TOKEN = "com.xabber.android.ui.OAuthVerifier.EXTRA_REFRESH_TOKEN"; - - private static final String SAVED_URL = "com.xabber.android.ui.OAuthActivity.URL"; - private static final String SAVED_CODE = "com.xabber.android.ui.OAuthActivity.CODE"; - - private WebView webView; - private String code; - private boolean loaded; - - @SuppressLint("SetJavaScriptEnabled") - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (isFinishing()) - return; - - setContentView(R.layout.oauth); - webView = (WebView) findViewById(R.id.webview); - webView.getSettings().setJavaScriptEnabled(true); - webView.setWebViewClient(new OAuthWebViewClient()); - loaded = false; - if (savedInstanceState == null) { - webView.loadUrl(OAuthManager.getInstance().getUrl( - getAccountProtocol(getIntent()))); - code = null; - } else { - webView.loadUrl(savedInstanceState.getString(SAVED_URL)); - code = savedInstanceState.getString(SAVED_CODE); - } - update(); - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putString(SAVED_URL, webView.getUrl()); - outState.putString(SAVED_CODE, code); - } - - private void update() { - boolean progress = !loaded || code != null; - webView.setVisibility(progress ? View.INVISIBLE : View.VISIBLE); - findViewById(R.id.progress_bar).setVisibility( - progress ? View.VISIBLE : View.GONE); - } - - private static Intent createResultIntent(Context context, - String refreshToken) { - Intent intent = new Intent(); - intent.putExtra(EXTRA_INVALIDATE, false); - intent.putExtra(EXTRA_REFRESH_TOKEN, refreshToken); - return intent; - } - - private static Intent createInvalidateIntent(Context context) { - Intent intent = new Intent(); - intent.putExtra(EXTRA_INVALIDATE, true); - return intent; - } - - public static Intent createIntent(Context context, AccountProtocol protocol) { - Intent intent = new Intent(context, OAuthActivity.class); - intent.setData(Uri.parse(String.valueOf(protocol.ordinal()))); - return intent; - } - - public static boolean isInvalidated(Intent intent) { - return intent.getBooleanExtra(OAuthActivity.EXTRA_INVALIDATE, false); - } - - public static String getToken(Intent intent) { - return intent.getStringExtra(OAuthActivity.EXTRA_REFRESH_TOKEN); - } - - private static AccountProtocol getAccountProtocol(Intent intent) { - int index = Integer.valueOf(intent.getData().toString()); - return AccountProtocol.values()[index]; - } - - private class OAuthWebViewClient extends WebViewClient { - - @Override - public void onPageFinished(WebView webView, String url) { - super.onPageFinished(webView, url); - LogManager.i(this, url); - if (code != null) - return; - loaded = true; - Uri uri = Uri.parse(url); - if (OAuthManager.getInstance().isValidUri( - getAccountProtocol(getIntent()), uri)) { - code = uri.getQueryParameter("code"); - if (code == null) { - setResult(RESULT_OK, - createInvalidateIntent(OAuthActivity.this)); - finish(); - } else { - new OAuthTokenRequester().execute(code); - } - } - update(); - } - } - - private class OAuthTokenRequester extends AsyncTask { - - @Override - protected String doInBackground(String... params) { - try { - return OAuthManager.getInstance().requestRefreshToken( - getAccountProtocol(getIntent()), params[0]); - } catch (NetworkException e) { - LogManager.exception(OAuthActivity.this, e); - return null; - } - } - - @Override - protected void onPostExecute(String result) { - super.onPostExecute(result); - setResult(RESULT_OK, createResultIntent(OAuthActivity.this, result)); - finish(); - } - - } + private static final String EXTRA_INVALIDATE = "com.xabber.android.ui.OAuthVerifier.EXTRA_CANCELED"; + private static final String EXTRA_REFRESH_TOKEN = "com.xabber.android.ui.OAuthVerifier.EXTRA_REFRESH_TOKEN"; + + private static final String SAVED_URL = "com.xabber.android.ui.OAuthActivity.URL"; + private static final String SAVED_CODE = "com.xabber.android.ui.OAuthActivity.CODE"; + + private WebView webView; + private String code; + private boolean loaded; + + private static Intent createResultIntent(Context context, + String refreshToken) { + Intent intent = new Intent(); + intent.putExtra(EXTRA_INVALIDATE, false); + intent.putExtra(EXTRA_REFRESH_TOKEN, refreshToken); + return intent; + } + + private static Intent createInvalidateIntent(Context context) { + Intent intent = new Intent(); + intent.putExtra(EXTRA_INVALIDATE, true); + return intent; + } + + public static Intent createIntent(Context context, AccountProtocol protocol) { + Intent intent = new Intent(context, OAuthActivity.class); + intent.setData(Uri.parse(String.valueOf(protocol.ordinal()))); + return intent; + } + + public static boolean isInvalidated(Intent intent) { + return intent.getBooleanExtra(OAuthActivity.EXTRA_INVALIDATE, false); + } + + public static String getToken(Intent intent) { + return intent.getStringExtra(OAuthActivity.EXTRA_REFRESH_TOKEN); + } + + private static AccountProtocol getAccountProtocol(Intent intent) { + int index = Integer.valueOf(intent.getData().toString()); + return AccountProtocol.values()[index]; + } + + @SuppressLint("SetJavaScriptEnabled") + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (isFinishing()) + return; + + setContentView(R.layout.oauth); + webView = (WebView) findViewById(R.id.webview); + webView.getSettings().setJavaScriptEnabled(true); + webView.setWebViewClient(new OAuthWebViewClient()); + loaded = false; + if (savedInstanceState == null) { + webView.loadUrl(OAuthManager.getInstance().getUrl( + getAccountProtocol(getIntent()))); + code = null; + } else { + webView.loadUrl(savedInstanceState.getString(SAVED_URL)); + code = savedInstanceState.getString(SAVED_CODE); + } + update(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putString(SAVED_URL, webView.getUrl()); + outState.putString(SAVED_CODE, code); + } + + private void update() { + boolean progress = !loaded || code != null; + webView.setVisibility(progress ? View.INVISIBLE : View.VISIBLE); + findViewById(R.id.progress_bar).setVisibility( + progress ? View.VISIBLE : View.GONE); + } + + private class OAuthWebViewClient extends WebViewClient { + + @Override + public void onPageFinished(WebView webView, String url) { + super.onPageFinished(webView, url); + LogManager.i(this, url); + if (code != null) + return; + loaded = true; + Uri uri = Uri.parse(url); + if (OAuthManager.getInstance().isValidUri( + getAccountProtocol(getIntent()), uri)) { + code = uri.getQueryParameter("code"); + if (code == null) { + setResult(RESULT_OK, + createInvalidateIntent(OAuthActivity.this)); + finish(); + } else { + new OAuthTokenRequester().execute(code); + } + } + update(); + } + } + + private class OAuthTokenRequester extends AsyncTask { + + @Override + protected String doInBackground(String... params) { + try { + return OAuthManager.getInstance().requestRefreshToken( + getAccountProtocol(getIntent()), params[0]); + } catch (NetworkException e) { + LogManager.exception(OAuthActivity.this, e); + return null; + } + } + + @Override + protected void onPostExecute(String result) { + super.onPostExecute(result); + setResult(RESULT_OK, createResultIntent(OAuthActivity.this, result)); + finish(); + } + + } } diff --git a/app/src/main/java/com/xabber/android/ui/OccupantList.java b/app/src/main/java/com/xabber/android/ui/OccupantList.java index 69bf1e2adc..47e52a0e57 100644 --- a/app/src/main/java/com/xabber/android/ui/OccupantList.java +++ b/app/src/main/java/com/xabber/android/ui/OccupantList.java @@ -1,25 +1,25 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.ui; -import java.util.Collection; - import android.content.Context; import android.content.Intent; import android.os.Bundle; +import android.support.v7.widget.Toolbar; +import com.xabber.android.R; import com.xabber.android.data.Application; import com.xabber.android.data.account.OnAccountChangedListener; import com.xabber.android.data.entity.BaseEntity; @@ -29,84 +29,88 @@ import com.xabber.android.data.roster.OnContactChangedListener; import com.xabber.android.ui.adapter.OccupantListAdapter; import com.xabber.android.ui.helper.ManagedListActivity; -import com.xabber.androiddev.R; import com.xabber.xmpp.address.Jid; +import java.util.Collection; + /** * Represent list of occupants in the room. - * + * * @author alexander.ivanov - * */ public class OccupantList extends ManagedListActivity implements - OnAccountChangedListener, OnContactChangedListener { - - private String account; - private String room; - private OccupantListAdapter listAdapter; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (isFinishing()) - return; - - account = getAccount(getIntent()); - room = Jid.getBareAddress(getUser(getIntent())); - if (account == null || room == null - || !MUCManager.getInstance().hasRoom(account, room)) { - Application.getInstance().onError(R.string.ENTRY_IS_NOT_FOUND); - finish(); - return; - } - setContentView(R.layout.list); - listAdapter = new OccupantListAdapter(this, account, room); - setListAdapter(listAdapter); - } - - @Override - protected void onResume() { - super.onResume(); - Application.getInstance().addUIListener(OnAccountChangedListener.class, - this); - Application.getInstance().addUIListener(OnContactChangedListener.class, - this); - listAdapter.onChange(); - } - - @Override - protected void onPause() { - super.onPause(); - Application.getInstance().removeUIListener( - OnAccountChangedListener.class, this); - Application.getInstance().removeUIListener( - OnContactChangedListener.class, this); - } - - @Override - public void onContactsChanged(Collection entities) { - if (entities.contains(new BaseEntity(account, room))) - listAdapter.onChange(); - } - - @Override - public void onAccountsChanged(Collection accounts) { - if (accounts.contains(account)) - listAdapter.onChange(); - } - - public static Intent createIntent(Context context, String account, - String user) { - return new EntityIntentBuilder(context, OccupantList.class) - .setAccount(account).setUser(user).build(); - } - - private static String getAccount(Intent intent) { - return AccountIntentBuilder.getAccount(intent); - } - - private static String getUser(Intent intent) { - return EntityIntentBuilder.getUser(intent); - } + OnAccountChangedListener, OnContactChangedListener { + + private String account; + private String room; + private OccupantListAdapter listAdapter; + + public static Intent createIntent(Context context, String account, + String user) { + return new EntityIntentBuilder(context, OccupantList.class) + .setAccount(account).setUser(user).build(); + } + + private static String getAccount(Intent intent) { + return AccountIntentBuilder.getAccount(intent); + } + + private static String getUser(Intent intent) { + return EntityIntentBuilder.getUser(intent); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (isFinishing()) + return; + + account = getAccount(getIntent()); + room = Jid.getBareAddress(getUser(getIntent())); + if (account == null || room == null + || !MUCManager.getInstance().hasRoom(account, room)) { + Application.getInstance().onError(R.string.ENTRY_IS_NOT_FOUND); + finish(); + return; + } + setContentView(R.layout.list); + setSupportActionBar((Toolbar) findViewById(R.id.toolbar_default)); + + listAdapter = new OccupantListAdapter(this, account, room); + setListAdapter(listAdapter); + + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + } + + @Override + protected void onResume() { + super.onResume(); + Application.getInstance().addUIListener(OnAccountChangedListener.class, + this); + Application.getInstance().addUIListener(OnContactChangedListener.class, + this); + listAdapter.onChange(); + } + + @Override + protected void onPause() { + super.onPause(); + Application.getInstance().removeUIListener( + OnAccountChangedListener.class, this); + Application.getInstance().removeUIListener( + OnContactChangedListener.class, this); + } + + @Override + public void onContactsChanged(Collection entities) { + if (entities.contains(new BaseEntity(account, room))) + listAdapter.onChange(); + } + + @Override + public void onAccountsChanged(Collection accounts) { + if (accounts.contains(account)) + listAdapter.onChange(); + } } diff --git a/app/src/main/java/com/xabber/android/ui/PasswordRequest.java b/app/src/main/java/com/xabber/android/ui/PasswordRequest.java index 20670a561c..af4e46b765 100644 --- a/app/src/main/java/com/xabber/android/ui/PasswordRequest.java +++ b/app/src/main/java/com/xabber/android/ui/PasswordRequest.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -21,65 +21,64 @@ import android.widget.CheckBox; import android.widget.EditText; +import com.xabber.android.R; import com.xabber.android.data.Application; import com.xabber.android.data.account.AccountManager; import com.xabber.android.data.intent.AccountIntentBuilder; import com.xabber.android.ui.helper.ManagedDialog; -import com.xabber.androiddev.R; /** * Dialog with password request for authentication. - * + * * @author alexander.ivanov - * */ public class PasswordRequest extends ManagedDialog { - private String account; + private String account; - private EditText passwordView; - private CheckBox storePasswordView; + private EditText passwordView; + private CheckBox storePasswordView; - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - findViewById(android.R.id.button3).setVisibility(View.GONE); - View layout = getLayoutInflater().inflate(R.layout.password_request, - null); - passwordView = (EditText) layout.findViewById(R.id.account_password); - storePasswordView = (CheckBox) layout.findViewById(R.id.store_password); - account = getAccount(getIntent()); - if (AccountManager.getInstance().getAccount(account) == null) { - Application.getInstance().onError(R.string.NO_SUCH_ACCOUNT); - finish(); - return; - } - setDialogTitle(AccountManager.getInstance().getVerboseName(account)); - setCustomView(layout, true); - } + public static Intent createIntent(Context context, String account) { + return new AccountIntentBuilder(context, PasswordRequest.class) + .setAccount(account).build(); + } - @Override - public void onAccept() { - super.onAccept(); - AccountManager.getInstance().setPassword(account, - storePasswordView.isChecked(), - passwordView.getText().toString()); - finish(); - } + private static String getAccount(Intent intent) { + return AccountIntentBuilder.getAccount(intent); + } - @Override - public void onDecline() { - AccountManager.getInstance().removePasswordRequest(account); - finish(); - } + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + findViewById(android.R.id.button3).setVisibility(View.GONE); + View layout = getLayoutInflater().inflate(R.layout.password_request, + null); + passwordView = (EditText) layout.findViewById(R.id.account_password); + storePasswordView = (CheckBox) layout.findViewById(R.id.store_password); + account = getAccount(getIntent()); + if (AccountManager.getInstance().getAccount(account) == null) { + Application.getInstance().onError(R.string.NO_SUCH_ACCOUNT); + finish(); + return; + } + setDialogTitle(AccountManager.getInstance().getVerboseName(account)); + setCustomView(layout, true); + } - public static Intent createIntent(Context context, String account) { - return new AccountIntentBuilder(context, PasswordRequest.class) - .setAccount(account).build(); - } + @Override + public void onAccept() { + super.onAccept(); + AccountManager.getInstance().setPassword(account, + storePasswordView.isChecked(), + passwordView.getText().toString()); + finish(); + } - private static String getAccount(Intent intent) { - return AccountIntentBuilder.getAccount(intent); - } + @Override + public void onDecline() { + AccountManager.getInstance().removePasswordRequest(account); + finish(); + } } diff --git a/app/src/main/java/com/xabber/android/ui/PhraseEditor.java b/app/src/main/java/com/xabber/android/ui/PhraseEditor.java deleted file mode 100644 index c40b4eae0e..0000000000 --- a/app/src/main/java/com/xabber/android/ui/PhraseEditor.java +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.xabber.android.ui; - -import java.util.HashMap; -import java.util.Map; -import java.util.regex.PatternSyntaxException; - -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.provider.Settings; -import android.widget.Toast; - -import com.xabber.android.data.Application; -import com.xabber.android.data.intent.SegmentIntentBuilder; -import com.xabber.android.data.message.phrase.Phrase; -import com.xabber.android.data.message.phrase.PhraseManager; -import com.xabber.android.ui.helper.BaseSettingsActivity; -import com.xabber.androiddev.R; - -public class PhraseEditor extends BaseSettingsActivity { - - private Phrase phrase; - - @Override - protected void onInflate(Bundle savedInstanceState) { - addPreferencesFromResource(R.xml.phrase_editor); - Integer index = getPhraseIndex(getIntent()); - if (index == null) { - phrase = null; - setTitle(R.string.phrase_add); - } else { - phrase = PhraseManager.getInstance().getPhrase(index); - if (phrase == null) { - finish(); - return; - } - String title = phrase.getText(); - if ("".equals(title)) - title = Application.getInstance().getString( - R.string.phrase_empty); - setTitle(title); - } - } - - @Override - protected Map getValues() { - Map source = new HashMap(); - putValue(source, R.string.phrase_text_key, - phrase == null ? "" : phrase.getText()); - putValue(source, R.string.phrase_user_key, - phrase == null ? "" : phrase.getUser()); - putValue(source, R.string.phrase_group_key, phrase == null ? "" - : phrase.getGroup()); - putValue(source, R.string.phrase_regexp_key, phrase == null ? false - : phrase.isRegexp()); - putValue(source, R.string.phrase_sound_key, - phrase == null ? Settings.System.DEFAULT_NOTIFICATION_URI - : phrase.getSound()); - return source; - } - - @Override - protected boolean setValues(Map source, - Map result) { - String text = getString(result, R.string.phrase_text_key); - String user = getString(result, R.string.phrase_user_key); - String group = getString(result, R.string.phrase_group_key); - boolean regexp = getBoolean(result, R.string.phrase_regexp_key); - Uri sound = getUri(result, R.string.phrase_sound_key); - if (regexp) - try { - Phrase.compile(text); - Phrase.compile(user); - Phrase.compile(group); - } catch (PatternSyntaxException e) { - Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show(); - return false; - } - if (phrase == null && "".equals(text) && "".equals(user) - && "".equals(group)) - return true; - PhraseManager.getInstance().updateOrCreatePhrase(phrase, text, user, - group, regexp, sound); - return true; - } - - public static Intent createIntent(Context context) { - return createIntent(context, null); - } - - public static Intent createIntent(Context context, Integer phraseIndex) { - SegmentIntentBuilder builder = new SegmentIntentBuilder>( - context, PhraseEditor.class); - if (phraseIndex != null) - builder.addSegment(phraseIndex.toString()); - return builder.build(); - } - - private Integer getPhraseIndex(Intent intent) { - String value = SegmentIntentBuilder.getSegment(intent, 0); - if (value == null) - return null; - else - return Integer.valueOf(value); - } - -} diff --git a/app/src/main/java/com/xabber/android/ui/PhraseList.java b/app/src/main/java/com/xabber/android/ui/PhraseList.java deleted file mode 100644 index 7ddb8c3a13..0000000000 --- a/app/src/main/java/com/xabber/android/ui/PhraseList.java +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.xabber.android.ui; - -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; - -import com.xabber.android.data.Application; -import com.xabber.android.data.message.phrase.PhraseManager; -import com.xabber.android.ui.adapter.BaseListEditorAdapter; -import com.xabber.android.ui.adapter.PhraseListAdapter; -import com.xabber.android.ui.helper.BaseListEditor; -import com.xabber.androiddev.R; - -public class PhraseList extends BaseListEditor { - - @Override - protected int getAddTextResourceId() { - return R.string.phrase_add; - } - - @Override - protected Intent getAddIntent() { - return PhraseEditor.createIntent(this); - } - - @Override - protected Intent getEditIntent(Integer actionWith) { - return PhraseEditor.createIntent(this, actionWith); - } - - @Override - protected int getRemoveTextResourceId() { - return R.string.phrase_delete; - } - - @Override - protected String getRemoveConfirmation(Integer actionWith) { - String text = PhraseManager.getInstance().getPhrase(actionWith) - .getText(); - if ("".equals(text)) - text = Application.getInstance().getString(R.string.phrase_empty); - return getString(R.string.phrase_delete_confirm, text); - } - - @Override - protected void removeItem(Integer actionWith) { - PhraseManager.getInstance().removePhrase(actionWith); - } - - @Override - protected BaseListEditorAdapter createListAdapter() { - return new PhraseListAdapter(this); - } - - @Override - protected Integer getSavedValue(Bundle bundle, String key) { - return bundle.getInt(key); - } - - @Override - protected void putSavedValue(Bundle bundle, String key, Integer actionWith) { - bundle.putInt(key, actionWith); - } - - public static Intent createIntent(Context context) { - return new Intent(context, PhraseList.class); - } - -} diff --git a/app/src/main/java/com/xabber/android/ui/PreferenceEditor.java b/app/src/main/java/com/xabber/android/ui/PreferenceEditor.java deleted file mode 100644 index 850fd29810..0000000000 --- a/app/src/main/java/com/xabber/android/ui/PreferenceEditor.java +++ /dev/null @@ -1,201 +0,0 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.xabber.android.ui; - -import android.app.Dialog; -import android.app.ProgressDialog; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.SharedPreferences.OnSharedPreferenceChangeListener; -import android.os.Bundle; -import android.preference.CheckBoxPreference; -import android.preference.Preference; -import android.preference.Preference.OnPreferenceClickListener; -import android.preference.PreferenceManager; -import android.preference.PreferenceScreen; - -import com.xabber.android.data.ActivityManager; -import com.xabber.android.data.Application; -import com.xabber.android.data.LogManager; -import com.xabber.android.data.SettingsManager; -import com.xabber.android.data.account.AccountManager; -import com.xabber.android.data.account.StatusMode; -import com.xabber.android.data.connection.CertificateManager; -import com.xabber.android.data.connection.ConnectionManager; -import com.xabber.android.data.roster.GroupManager; -import com.xabber.android.ui.dialog.ConfirmDialogBuilder; -import com.xabber.android.ui.dialog.ConfirmDialogListener; -import com.xabber.android.ui.dialog.DialogBuilder; -import com.xabber.android.ui.helper.ManagedPreferenceActivity; -import com.xabber.android.ui.helper.PreferenceSummaryHelper; -import com.xabber.androiddev.R; - -public class PreferenceEditor extends ManagedPreferenceActivity implements - OnPreferenceClickListener, OnSharedPreferenceChangeListener, - ConfirmDialogListener { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (isFinishing()) - return; - - addPreferencesFromResource(R.xml.preference_editor); - - getPreferenceScreen().findPreference( - getString(R.string.preference_accounts_key)).setIntent( - AccountList.createIntent(this)); - getPreferenceScreen() - .findPreference(getString(R.string.events_phrases)).setIntent( - PhraseList.createIntent(this)); - - getPreferenceScreen().findPreference( - getString(R.string.cache_clear_key)) - .setOnPreferenceClickListener(this); - getPreferenceScreen().findPreference( - getString(R.string.security_clear_certificate_key)) - .setOnPreferenceClickListener(this); - getPreferenceScreen().findPreference( - getString(R.string.contacts_reset_offline_key)) - .setOnPreferenceClickListener(this); - getPreferenceScreen().findPreference(getString(R.string.debug_log_key)) - .setEnabled(LogManager.isDebugable()); - - // Force request sound. This will set default value if not specified. - SettingsManager.eventsSound(); - SettingsManager.chatsAttentionSound(); - - PreferenceScreen about = (PreferenceScreen) getPreferenceScreen() - .findPreference(getString(R.string.preference_about_key)); - about.setSummary(getString(R.string.application_name) + "\n" - + getString(R.string.application_version)); - about.setIntent(AboutViewer.createIntent(this)); - PreferenceSummaryHelper.updateSummary(getPreferenceScreen()); - } - - @Override - protected void onResume() { - super.onResume(); - PreferenceManager.getDefaultSharedPreferences(this) - .registerOnSharedPreferenceChangeListener(this); - } - - @Override - protected void onPause() { - super.onPause(); - PreferenceManager.getDefaultSharedPreferences(this) - .unregisterOnSharedPreferenceChangeListener(this); - } - - private void changeGrouping() { - boolean grouped = SettingsManager.contactsShowAccounts() - || SettingsManager.contactsShowGroups(); - ((CheckBoxPreference) getPreferenceScreen().findPreference( - getString(R.string.contacts_stay_active_chats_key))) - .setChecked(grouped); - ((CheckBoxPreference) getPreferenceScreen().findPreference( - getString(R.string.contacts_show_empty_groups_key))) - .setEnabled(grouped); - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, - String key) { - if (key.equals(getString(R.string.contacts_show_accounts_key))) { - changeGrouping(); - } else if (key.equals(getString(R.string.contacts_show_groups_key))) { - changeGrouping(); - } else if (key.equals(getString(R.string.interface_theme_key))) { - ActivityManager.getInstance().clearStack(true); - startActivity(ContactList.createIntent(this)); - } - } - - @Override - public boolean onPreferenceClick(Preference preference) { - if (preference.getKey().equals(getString(R.string.cache_clear_key))) { - showDialog(R.string.cache_clear_warning); - } else if (preference.getKey().equals( - getString(R.string.security_clear_certificate_key))) { - showDialog(R.string.security_clear_certificate_warning); - } else if (preference.getKey().equals( - getString(R.string.contacts_reset_offline_key))) { - showDialog(R.string.contacts_reset_offline_warning); - } - return false; - } - - @Override - protected Dialog onCreateDialog(int id) { - super.onCreateDialog(id); - switch (id) { - case R.string.cache_clear_warning: - return new ConfirmDialogBuilder(this, R.string.cache_clear_warning, - this).setMessage(R.string.cache_clear_warning).create(); - case R.string.security_clear_certificate_warning: - return new ConfirmDialogBuilder(this, - R.string.security_clear_certificate_warning, this) - .setMessage(R.string.security_clear_certificate_warning) - .create(); - case R.string.contacts_reset_offline_warning: - return new ConfirmDialogBuilder(this, - R.string.contacts_reset_offline_warning, this).setMessage( - R.string.contacts_reset_offline_warning).create(); - case R.string.application_state_closing: - ProgressDialog progressDialog = new ProgressDialog(this); - progressDialog - .setMessage(getString(R.string.application_state_closing)); - progressDialog.setCancelable(false); - progressDialog.setIndeterminate(true); - return progressDialog; - default: - return null; - } - } - - @Override - public void onAccept(DialogBuilder dialogBuilder) { - switch (dialogBuilder.getDialogId()) { - case R.string.cache_clear_warning: - AccountManager.getInstance() - .setStatus(StatusMode.unavailable, null); - ((Application) getApplication()).requestToClear(); - Application.getInstance().requestToClose(); - showDialog(R.string.application_state_closing); - break; - case R.string.security_clear_certificate_warning: - CertificateManager.getInstance().removeCertificates(); - ConnectionManager.getInstance().updateConnections(true); - break; - case R.string.contacts_reset_offline_warning: - GroupManager.getInstance().resetShowOfflineModes(); - break; - } - } - - @Override - public void onDecline(DialogBuilder dialogBuilder) { - } - - @Override - public void onCancel(DialogBuilder dialogBuilder) { - } - - public static Intent createIntent(Context context) { - return new Intent(context, PreferenceEditor.class); - } - -} diff --git a/app/src/main/java/com/xabber/android/ui/QuestionViewer.java b/app/src/main/java/com/xabber/android/ui/QuestionViewer.java index 2556152342..bb4e61818b 100644 --- a/app/src/main/java/com/xabber/android/ui/QuestionViewer.java +++ b/app/src/main/java/com/xabber/android/ui/QuestionViewer.java @@ -1,29 +1,29 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.ui; -import java.util.Collection; - import android.content.Context; import android.content.Intent; import android.os.Bundle; +import android.support.v7.widget.Toolbar; import android.view.View; import android.view.View.OnClickListener; import android.widget.EditText; import android.widget.TextView; +import com.xabber.android.R; import com.xabber.android.data.Application; import com.xabber.android.data.NetworkException; import com.xabber.android.data.account.AccountManager; @@ -35,189 +35,182 @@ import com.xabber.android.data.roster.AbstractContact; import com.xabber.android.data.roster.OnContactChangedListener; import com.xabber.android.data.roster.RosterManager; -import com.xabber.android.ui.helper.ContactTitleInflater; +import com.xabber.android.ui.helper.ContactTitleActionBarInflater; import com.xabber.android.ui.helper.ManagedActivity; -import com.xabber.androiddev.R; import com.xabber.xmpp.address.Jid; +import java.util.Collection; + /** * Represents OTR question. - * + * * @author alexander.ivanov - * */ public class QuestionViewer extends ManagedActivity implements - OnAccountChangedListener, OnContactChangedListener, OnClickListener { - - private static final String EXTRA_FIELD_SHOW_QUESTION = "com.xabber.android.data.ui.QuestionViewer.SHOW_QUESTION"; - private static final String EXTRA_FIELD_ANSWER_REQUEST = "com.xabber.android.data.ui.QuestionViewer.ANSWER_REQUEST"; - private static final String EXTRA_FIELD_CANCEL = "com.xabber.android.data.ui.QuestionViewer.CANCEL"; - - private String account; - private String user; - private boolean showQuestion; - private boolean answerRequest; - private EditText questionView; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (isFinishing()) - return; - - Intent intent = getIntent(); - account = QuestionViewer.getAccount(intent); - user = QuestionViewer.getUser(intent); - if (AccountManager.getInstance().getAccount(account) == null - || user == null) { - Application.getInstance().onError(R.string.ENTRY_IS_NOT_FOUND); - finish(); - return; - } - if (intent.getBooleanExtra(EXTRA_FIELD_CANCEL, false)) { - try { - OTRManager.getInstance().abortSmp(account, user); - } catch (NetworkException e) { - Application.getInstance().onError(e); - } - finish(); - return; - } - showQuestion = intent.getBooleanExtra(EXTRA_FIELD_SHOW_QUESTION, true); - answerRequest = intent.getBooleanExtra(EXTRA_FIELD_ANSWER_REQUEST, - false); - if (showQuestion) { - setContentView(R.layout.question_viewer); - questionView = (EditText) findViewById(R.id.question); - questionView.setEnabled(!answerRequest); - if (answerRequest) - questionView.setText(intent.getStringExtra(Intent.EXTRA_TEXT)); - else - findViewById(R.id.cancel).setVisibility(View.GONE); - } else - setContentView(R.layout.secret_viewer); - findViewById(R.id.cancel).setOnClickListener(this); - findViewById(R.id.send).setOnClickListener(this); - } - - @Override - protected void onResume() { - super.onResume(); - Application.getInstance().addUIListener(OnAccountChangedListener.class, - this); - Application.getInstance().addUIListener(OnContactChangedListener.class, - this); - update(); - } - - @Override - protected void onPause() { - super.onPause(); - Application.getInstance().removeUIListener( - OnAccountChangedListener.class, this); - Application.getInstance().removeUIListener( - OnContactChangedListener.class, this); - } - - @Override - public void onContactsChanged(Collection entities) { - String thisBareAddress = Jid.getBareAddress(user); - for (BaseEntity entity : entities) - if (entity.equals(account, thisBareAddress)) { - update(); - break; - } - } - - @Override - public void onAccountsChanged(Collection accounts) { - if (accounts.contains(account)) - update(); - } - - @Override - public void onClick(View view) { - switch (view.getId()) { - case R.id.send: - String question = showQuestion ? questionView.getText().toString() - : null; - String answer = ((TextView) findViewById(R.id.answer)).getText() - .toString(); - try { - if (answerRequest) - OTRManager.getInstance().respondSmp(account, user, - question, answer); - else - OTRManager.getInstance().initSmp(account, user, question, - answer); - } catch (NetworkException e) { - Application.getInstance().onError(e); - } - finish(); - break; - case R.id.cancel: - try { - OTRManager.getInstance().abortSmp(account, user); - } catch (NetworkException e) { - Application.getInstance().onError(e); - } - finish(); - default: - break; - } - } - - private void update() { - AbstractContact abstractContact = RosterManager.getInstance() - .getBestContact(account, user); - ContactTitleInflater.updateTitle(findViewById(R.id.title), this, - abstractContact); - } - - /** - * @param context - * @param account - * @param user - * @return Intent to cancel negotiation. - */ - public static Intent createCanelIntent(Context context, String account, - String user) { - Intent intent = new EntityIntentBuilder(context, QuestionViewer.class) - .setAccount(account).setUser(user).build(); - intent.putExtra(EXTRA_FIELD_CANCEL, true); - return intent; - } - - /** - * @param context - * @param account - * @param user - * @param showQuestion - * false is used for shared secret. - * @param answerRequest - * false is used to ask a question. - * @param question - * must be not null if showQuestion and - * answerRequest are true. - * @return - */ - public static Intent createIntent(Context context, String account, - String user, boolean showQuestion, boolean answerRequest, - String question) { - Intent intent = new EntityIntentBuilder(context, QuestionViewer.class) - .setAccount(account).setUser(user).build(); - intent.putExtra(EXTRA_FIELD_SHOW_QUESTION, showQuestion); - intent.putExtra(EXTRA_FIELD_ANSWER_REQUEST, answerRequest); - intent.putExtra(Intent.EXTRA_TEXT, question); - return intent; - } - - private static String getAccount(Intent intent) { - return AccountIntentBuilder.getAccount(intent); - } - - private static String getUser(Intent intent) { - return EntityIntentBuilder.getUser(intent); - } + OnAccountChangedListener, OnContactChangedListener, OnClickListener { + + private static final String EXTRA_FIELD_SHOW_QUESTION = "com.xabber.android.data.ui.QuestionViewer.SHOW_QUESTION"; + private static final String EXTRA_FIELD_ANSWER_REQUEST = "com.xabber.android.data.ui.QuestionViewer.ANSWER_REQUEST"; + private static final String EXTRA_FIELD_CANCEL = "com.xabber.android.data.ui.QuestionViewer.CANCEL"; + ContactTitleActionBarInflater contactTitleActionBarInflater; + private String account; + private String user; + private boolean showQuestion; + private boolean answerRequest; + private EditText questionView; + + /** + * @param context + * @param account + * @param user + * @return Intent to cancel negotiation. + */ + public static Intent createCancelIntent(Context context, String account, String user) { + Intent intent = new EntityIntentBuilder(context, QuestionViewer.class) + .setAccount(account).setUser(user).build(); + intent.putExtra(EXTRA_FIELD_CANCEL, true); + return intent; + } + + /** + * @param context + * @param account + * @param user + * @param showQuestion false is used for shared secret. + * @param answerRequest false is used to ask a question. + * @param question must be not null if showQuestion and + * answerRequest are true. + * @return + */ + public static Intent createIntent(Context context, String account, String user, + boolean showQuestion, boolean answerRequest, String question) { + Intent intent = new EntityIntentBuilder(context, QuestionViewer.class) + .setAccount(account).setUser(user).build(); + intent.putExtra(EXTRA_FIELD_SHOW_QUESTION, showQuestion); + intent.putExtra(EXTRA_FIELD_ANSWER_REQUEST, answerRequest); + intent.putExtra(Intent.EXTRA_TEXT, question); + return intent; + } + + private static String getAccount(Intent intent) { + return AccountIntentBuilder.getAccount(intent); + } + + private static String getUser(Intent intent) { + return EntityIntentBuilder.getUser(intent); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (isFinishing()) { + return; + } + + Intent intent = getIntent(); + account = QuestionViewer.getAccount(intent); + user = QuestionViewer.getUser(intent); + if (AccountManager.getInstance().getAccount(account) == null || user == null) { + Application.getInstance().onError(R.string.ENTRY_IS_NOT_FOUND); + finish(); + return; + } + if (intent.getBooleanExtra(EXTRA_FIELD_CANCEL, false)) { + try { + OTRManager.getInstance().abortSmp(account, user); + } catch (NetworkException e) { + Application.getInstance().onError(e); + } + finish(); + return; + } + showQuestion = intent.getBooleanExtra(EXTRA_FIELD_SHOW_QUESTION, true); + answerRequest = intent.getBooleanExtra(EXTRA_FIELD_ANSWER_REQUEST, false); + if (showQuestion) { + setContentView(R.layout.question_viewer); + questionView = (EditText) findViewById(R.id.question); + questionView.setEnabled(!answerRequest); + if (answerRequest) { + questionView.setText(intent.getStringExtra(Intent.EXTRA_TEXT)); + } else { + findViewById(R.id.cancel).setVisibility(View.GONE); + } + } else { + setContentView(R.layout.secret_viewer); + + } + findViewById(R.id.cancel).setOnClickListener(this); + findViewById(R.id.send).setOnClickListener(this); + + contactTitleActionBarInflater = new ContactTitleActionBarInflater(this, (Toolbar) findViewById(R.id.toolbar_default)); + contactTitleActionBarInflater.setUpActionBarView(); + } + + @Override + protected void onResume() { + super.onResume(); + Application.getInstance().addUIListener(OnAccountChangedListener.class, this); + Application.getInstance().addUIListener(OnContactChangedListener.class, this); + update(); + } + + @Override + protected void onPause() { + super.onPause(); + Application.getInstance().removeUIListener(OnAccountChangedListener.class, this); + Application.getInstance().removeUIListener(OnContactChangedListener.class, this); + } + + @Override + public void onContactsChanged(Collection entities) { + String thisBareAddress = Jid.getBareAddress(user); + for (BaseEntity entity : entities) { + if (entity.equals(account, thisBareAddress)) { + update(); + break; + } + } + } + + @Override + public void onAccountsChanged(Collection accounts) { + if (accounts.contains(account)) { + update(); + } + } + + @Override + public void onClick(View view) { + switch (view.getId()) { + case R.id.send: + String question = showQuestion ? questionView.getText().toString() : null; + String answer = ((TextView) findViewById(R.id.answer)).getText().toString(); + try { + if (answerRequest) { + OTRManager.getInstance().respondSmp(account, user, question, answer); + } else { + OTRManager.getInstance().initSmp(account, user, question, answer); + } + } catch (NetworkException e) { + Application.getInstance().onError(e); + } + finish(); + break; + case R.id.cancel: + try { + OTRManager.getInstance().abortSmp(account, user); + } catch (NetworkException e) { + Application.getInstance().onError(e); + } + finish(); + default: + break; + } + } + + private void update() { + AbstractContact abstractContact = RosterManager.getInstance().getBestContact(account, user); + contactTitleActionBarInflater.update(abstractContact); + } } diff --git a/app/src/main/java/com/xabber/android/ui/RecentChatFragment.java b/app/src/main/java/com/xabber/android/ui/RecentChatFragment.java new file mode 100644 index 0000000000..1bc421b7e3 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/RecentChatFragment.java @@ -0,0 +1,135 @@ +package com.xabber.android.ui; + +import android.app.Activity; +import android.app.ListFragment; +import android.os.Bundle; +import android.support.v4.app.NavUtils; +import android.support.v7.widget.Toolbar; +import android.view.LayoutInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ListView; +import android.widget.Toast; + +import com.xabber.android.R; +import com.xabber.android.data.message.AbstractChat; +import com.xabber.android.ui.adapter.ChatListAdapter; +import com.xabber.android.ui.helper.AccountPainter; + +import java.util.ArrayList; +import java.util.List; + +public class RecentChatFragment extends ListFragment implements Toolbar.OnMenuItemClickListener { + private RecentChatFragmentInteractionListener listener; + + /** + * Mandatory empty constructor for the fragment manager to instantiate the + * fragment (e.g. upon screen orientation changes). + */ + public RecentChatFragment() { + } + + public static RecentChatFragment newInstance() { + return new RecentChatFragment(); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setListAdapter(new ChatListAdapter(getActivity())); + } + + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + + try { + listener = (RecentChatFragmentInteractionListener) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity.toString() + + " must implement RecentChatFragmentInteractionListener"); + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + super.onCreateView(inflater, container, savedInstanceState); + View rootView = inflater.inflate(R.layout.recent_chats, container, false); + + ArrayList activeChats = ((ChatViewer) getActivity()).getChatViewerAdapter().getActiveChats(); + ((ChatListAdapter) getListAdapter()).updateChats(activeChats); + + if (getListAdapter().isEmpty()) { + Activity activity = getActivity(); + Toast.makeText(activity, R.string.chat_list_is_empty, Toast.LENGTH_LONG).show(); + activity.finish(); + } + + Toolbar toolbar = (Toolbar) rootView.findViewById(R.id.toolbar_default); + toolbar.setTitle(R.string.recent_chats); + toolbar.setNavigationIcon(R.drawable.ic_arrow_left_white_24dp); + toolbar.setNavigationOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + NavUtils.navigateUpFromSameTask(getActivity()); + } + }); + toolbar.inflateMenu(R.menu.recent_chats); + toolbar.setOnMenuItemClickListener(this); + + AccountPainter accountPainter = new AccountPainter(getActivity()); + toolbar.setBackgroundColor(accountPainter.getDefaultMainColor()); + + return rootView; + } + + @Override + public void onResume() { + super.onResume(); + + ((ChatViewer)getActivity()).registerRecentChatsList(this); + } + + @Override + public void onPause() { + super.onPause(); + + ((ChatViewer)getActivity()).unregisterRecentChatsList(this); + } + + @Override + public void onDetach() { + listener = null; + super.onDetach(); + } + + + @Override + public void onListItemClick(ListView l, View v, int position, long id) { + super.onListItemClick(l, v, position, id); + + if (null != listener) { + listener.onChatSelected((AbstractChat) getListAdapter().getItem(position)); + } + } + + @Override + public boolean onMenuItemClick(MenuItem item) { + if (item.getItemId() == R.id.action_under_construction) { + Toast.makeText(getActivity(), getActivity().getString(R.string.under_construction_message), Toast.LENGTH_SHORT).show(); + } + + return false; + } + + public void updateChats(List chats) { + ((ChatListAdapter) getListAdapter()).updateChats(chats); + } + + public interface RecentChatFragmentInteractionListener { + void onChatSelected(AbstractChat chat); + } +} diff --git a/app/src/main/java/com/xabber/android/ui/ReconnectionActivity.java b/app/src/main/java/com/xabber/android/ui/ReconnectionActivity.java index ec319ed66c..92e9768789 100644 --- a/app/src/main/java/com/xabber/android/ui/ReconnectionActivity.java +++ b/app/src/main/java/com/xabber/android/ui/ReconnectionActivity.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -24,23 +24,22 @@ /** * Activity launched from notification bar to reconnect disconnected accounts. - * + * * @author alexander.ivanov - * */ public class ReconnectionActivity extends Activity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - LogManager.i(this, "onReconnect"); - ConnectionManager.getInstance().updateConnections(false); - startActivity(ContactList.createPersistentIntent(this)); - finish(); - } + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + LogManager.i(this, "onReconnect"); + ConnectionManager.getInstance().updateConnections(false); + startActivity(ContactList.createPersistentIntent(this)); + finish(); + } - public static Intent createIntent(Context context) { - return new Intent(context, ReconnectionActivity.class); - } + public static Intent createIntent(Context context) { + return new Intent(context, ReconnectionActivity.class); + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/ui/StatusEditor.java b/app/src/main/java/com/xabber/android/ui/StatusEditor.java index bd368480d1..866f53c463 100644 --- a/app/src/main/java/com/xabber/android/ui/StatusEditor.java +++ b/app/src/main/java/com/xabber/android/ui/StatusEditor.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -17,20 +17,22 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; +import android.support.v7.widget.Toolbar; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.LayoutInflater; import android.view.Menu; +import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.AdapterView.OnItemClickListener; -import android.widget.Button; import android.widget.EditText; import android.widget.ListView; import android.widget.Spinner; +import com.xabber.android.R; import com.xabber.android.data.Application; import com.xabber.android.data.SettingsManager; import com.xabber.android.data.account.AccountItem; @@ -40,210 +42,222 @@ import com.xabber.android.data.intent.AccountIntentBuilder; import com.xabber.android.ui.adapter.StatusEditorAdapter; import com.xabber.android.ui.adapter.StatusModeAdapter; -import com.xabber.android.ui.adapter.UpdatableAdapter; +import com.xabber.android.ui.helper.BarPainter; import com.xabber.android.ui.helper.ManagedListActivity; -import com.xabber.androiddev.R; - -public class StatusEditor extends ManagedListActivity implements - View.OnClickListener, OnItemClickListener { - - private static final String SAVED_TEXT = "com.xabber.android.ui.StatusEditor.SAVED_TEXT"; - private static final String SAVED_MODE = "com.xabber.android.ui.StatusEditor.SAVED_MODE"; - - static final public int OPTION_MENU_CLEAR_STATUSES_ID = 1; - - static final public int CONTEXT_MENU_SELECT_STATUS_ID = 10; - static final public int CONTEXT_MENU_EDIT_STATUS_ID = 11; - static final public int CONTEXT_MENU_REMOVE_STATUS_ID = 12; - - private String account; - private Spinner statusModeView; - private EditText statusTextView; - - private SavedStatus actionWithItem; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (isFinishing()) - return; - - actionWithItem = null; - - setContentView(R.layout.status_editor); - - Intent intent = getIntent(); - account = StatusEditor.getAccount(intent); - if (account == null) - setTitle(getString(R.string.status_editor)); - else - setTitle(getString(R.string.status_editor_for, AccountManager - .getInstance().getVerboseName(account))); - - ListView listView = getListView(); - LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE); - View header = inflater.inflate(R.layout.status_editor_header, listView, - false); - listView.addHeaderView(header, null, false); - listView.setOnItemClickListener(this); - registerForContextMenu(listView); - setListAdapter(new StatusEditorAdapter(this)); - - statusTextView = (EditText) header.findViewById(R.id.status_text); - statusModeView = (Spinner) header.findViewById(R.id.status_mode); - statusModeView.setAdapter(new StatusModeAdapter(this)); - ((Button) findViewById(R.id.ok)).setOnClickListener(this); - - StatusMode statusMode; - String statusText; - if (savedInstanceState == null) { - if (account == null) { - statusMode = SettingsManager.statusMode(); - statusText = SettingsManager.statusText(); - } else { - AccountItem accountItem = AccountManager.getInstance() - .getAccount(account); - if (accountItem == null) { - Application.getInstance().onError(R.string.NO_SUCH_ACCOUNT); - finish(); - return; - } - statusMode = accountItem.getFactualStatusMode(); - statusText = accountItem.getStatusText(); - } - } else { - statusMode = StatusMode.valueOf(savedInstanceState - .getString(SAVED_MODE)); - statusText = savedInstanceState.getString(SAVED_TEXT); - } - showStatus(statusMode, statusText); - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - StatusMode statusMode = (StatusMode) statusModeView.getSelectedItem(); - outState.putString(SAVED_MODE, statusMode.name()); - outState.putString(SAVED_TEXT, statusTextView.getText().toString()); - } - - private void setStatus(StatusMode statusMode, String statusText) { - AccountManager accountManager = AccountManager.getInstance(); - if (account != null) - accountManager.setStatus(account, statusMode, statusText); - else { - accountManager.setStatus(statusMode, statusText); - } - } - - private void showStatus(StatusMode statusMode, String statusText) { - for (int index = 0; index < statusModeView.getCount(); index++) - if (statusMode == statusModeView.getAdapter().getItem(index)) - statusModeView.setSelection(index); - statusTextView.setText(statusText); - } - - @Override - protected void onResume() { - super.onResume(); - ((UpdatableAdapter) getListAdapter()).onChange(); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - menu.add(0, OPTION_MENU_CLEAR_STATUSES_ID, 0, - getResources().getText(R.string.clear_statuses)).setIcon( - android.R.drawable.ic_menu_close_clear_cancel); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - super.onOptionsItemSelected(item); - switch (item.getItemId()) { - case OPTION_MENU_CLEAR_STATUSES_ID: - AccountManager.getInstance().clearSavedStatuses(); - ((UpdatableAdapter) getListAdapter()).onChange(); - return true; - } - return false; - } - - @Override - public void onCreateContextMenu(ContextMenu menu, View v, - ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, v, menuInfo); - AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; - actionWithItem = (SavedStatus) getListView().getItemAtPosition( - info.position); - if (actionWithItem == null) // Header - return; - menu.add(0, CONTEXT_MENU_SELECT_STATUS_ID, 0, - getResources().getText(R.string.select_status)); - menu.add(0, CONTEXT_MENU_EDIT_STATUS_ID, 0, - getResources().getText(R.string.edit_status)); - menu.add(0, CONTEXT_MENU_REMOVE_STATUS_ID, 0, - getResources().getText(R.string.remove_status)); - } - - @Override - public boolean onContextItemSelected(MenuItem item) { - super.onContextItemSelected(item); - switch (item.getItemId()) { - case CONTEXT_MENU_SELECT_STATUS_ID: - setStatus(actionWithItem.getStatusMode(), - actionWithItem.getStatusText()); - finish(); - return true; - case CONTEXT_MENU_EDIT_STATUS_ID: - showStatus(actionWithItem.getStatusMode(), - actionWithItem.getStatusText()); - return true; - case CONTEXT_MENU_REMOVE_STATUS_ID: - AccountManager.getInstance().removeSavedStatus(actionWithItem); - ((UpdatableAdapter) getListAdapter()).onChange(); - return true; - } - return false; - } - - @Override - public void onClick(View v) { - switch (v.getId()) { - case R.id.ok: - StatusMode statusMode = (StatusMode) statusModeView - .getSelectedItem(); - String statusText = statusTextView.getText().toString(); - setStatus(statusMode, statusText); - finish(); - break; - default: - break; - } - } - - @Override - public void onItemClick(AdapterView parent, View view, int position, - long id) { - SavedStatus savedStatus = (SavedStatus) parent.getAdapter().getItem( - position); - if (savedStatus == null) // Header - return; - setStatus(savedStatus.getStatusMode(), savedStatus.getStatusText()); - finish(); - } - - public static Intent createIntent(Context context) { - return StatusEditor.createIntent(context, null); - } - - public static Intent createIntent(Context context, String account) { - return new AccountIntentBuilder(context, StatusEditor.class) - .setAccount(account).build(); - } - - private static String getAccount(Intent intent) { - return AccountIntentBuilder.getAccount(intent); - } + +public class StatusEditor extends ManagedListActivity implements OnItemClickListener, Toolbar.OnMenuItemClickListener, View.OnClickListener { + + private static final String SAVED_TEXT = "com.xabber.android.ui.StatusEditor.SAVED_TEXT"; + private static final String SAVED_MODE = "com.xabber.android.ui.StatusEditor.SAVED_MODE"; + + private String account; + private Spinner statusModeView; + private EditText statusTextView; + + private SavedStatus actionWithItem; + private StatusEditorAdapter adapter; + private View savedStatusesTextView; + + public static Intent createIntent(Context context) { + return StatusEditor.createIntent(context, null); + } + + public static Intent createIntent(Context context, String account) { + return new AccountIntentBuilder(context, StatusEditor.class).setAccount(account).build(); + } + + private static String getAccount(Intent intent) { + return AccountIntentBuilder.getAccount(intent); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (isFinishing()) { + return; + } + + actionWithItem = null; + + setContentView(R.layout.status_editor); + + Toolbar toolbar = (Toolbar) findViewById(R.id.top_toolbar); + toolbar.setNavigationIcon(R.drawable.ic_clear_white_24dp); + setTitle(null); + setSupportActionBar(toolbar); + + Intent intent = getIntent(); + account = StatusEditor.getAccount(intent); + + BarPainter barPainter = new BarPainter(this, toolbar); + + if (account != null) { + barPainter.updateWithAccountName(account); + } else { + barPainter.setDefaultColor(); + } + + ListView listView = getListView(); + listView.setOnItemClickListener(this); + registerForContextMenu(listView); + adapter = new StatusEditorAdapter(this); + + View footerView = ((LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.status_history_footer, null, false); + footerView.findViewById(R.id.clear_status_history_button).setOnClickListener(this); + listView.addFooterView(footerView); + + setListAdapter(adapter); + + statusTextView = (EditText) findViewById(R.id.status_text); + statusModeView = (Spinner) findViewById(R.id.status_icon); + statusModeView.setAdapter(new StatusModeAdapter(this)); + + savedStatusesTextView = findViewById(R.id.saved_statuses_textview); + + StatusMode statusMode; + String statusText; + if (savedInstanceState == null) { + if (account == null) { + statusMode = SettingsManager.statusMode(); + statusText = SettingsManager.statusText(); + } else { + AccountItem accountItem = AccountManager.getInstance().getAccount(account); + if (accountItem == null) { + Application.getInstance().onError(R.string.NO_SUCH_ACCOUNT); + finish(); + return; + } + statusMode = accountItem.getFactualStatusMode(); + statusText = accountItem.getStatusText(); + } + } else { + statusMode = StatusMode.valueOf(savedInstanceState.getString(SAVED_MODE)); + statusText = savedInstanceState.getString(SAVED_TEXT); + } + showStatus(statusMode, statusText); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + StatusMode statusMode = (StatusMode) statusModeView.getSelectedItem(); + outState.putString(SAVED_MODE, statusMode.name()); + outState.putString(SAVED_TEXT, statusTextView.getText().toString()); + } + + private void setStatus(StatusMode statusMode, String statusText) { + AccountManager accountManager = AccountManager.getInstance(); + if (account != null) { + accountManager.setStatus(account, statusMode, statusText); + } else { + accountManager.setStatus(statusMode, statusText); + } + } + + private void showStatus(StatusMode statusMode, String statusText) { + for (int index = 0; index < statusModeView.getCount(); index++) { + if (statusMode == statusModeView.getAdapter().getItem(index)) { + statusModeView.setSelection(index); + } + } + statusTextView.setText(statusText); + } + + @Override + protected void onResume() { + super.onResume(); + adapter.onChange(); + setStatusHistoryVisibility(); + } + + private void setStatusHistoryVisibility() { + boolean isHistoryEmpty = AccountManager.getInstance().getSavedStatuses().isEmpty(); + int visibility = isHistoryEmpty ? View.GONE : View.VISIBLE; + + getListView().setVisibility(visibility); + savedStatusesTextView.setVisibility(visibility); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + getMenuInflater().inflate(R.menu.set_status, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + super.onOptionsItemSelected(item); + switch (item.getItemId()) { + case R.id.action_change_status: + changeStatus(); + return true; + } + return false; + } + + private void clearStatusHistory() { + AccountManager.getInstance().clearSavedStatuses(); + adapter.onChange(); + setStatusHistoryVisibility(); + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; + actionWithItem = (SavedStatus) getListView().getItemAtPosition(info.position); + + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.status_context_menu, menu); + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + super.onContextItemSelected(item); + switch (item.getItemId()) { + case R.id.action_select_status: + setStatus(actionWithItem.getStatusMode(), actionWithItem.getStatusText()); + finish(); + return true; + case R.id.action_edit_status: + showStatus(actionWithItem.getStatusMode(), actionWithItem.getStatusText()); + statusTextView.requestFocus(); + return true; + case R.id.action_remove_status: + AccountManager.getInstance().removeSavedStatus(actionWithItem); + adapter.onChange(); + setStatusHistoryVisibility(); + return true; + } + return false; + } + + private void changeStatus() { + StatusMode statusMode = (StatusMode) statusModeView.getSelectedItem(); + String statusText = statusTextView.getText().toString(); + setStatus(statusMode, statusText); + finish(); + } + + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + SavedStatus savedStatus = (SavedStatus) parent.getAdapter().getItem(position); + setStatus(savedStatus.getStatusMode(), savedStatus.getStatusText()); + finish(); + } + + @Override + public boolean onMenuItemClick(MenuItem menuItem) { + return onOptionsItemSelected(menuItem); + } + + @Override + public void onClick(View v) { + switch (v.getId()) { + case R.id.clear_status_history_button: + clearStatusHistory(); + default: + } + } } diff --git a/app/src/main/java/com/xabber/android/ui/adapter/AccountActionButtonsAdapter.java b/app/src/main/java/com/xabber/android/ui/adapter/AccountActionButtonsAdapter.java new file mode 100644 index 0000000000..c045b70eb9 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/adapter/AccountActionButtonsAdapter.java @@ -0,0 +1,142 @@ +package com.xabber.android.ui.adapter; + +import android.app.Activity; +import android.content.res.Resources; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.LinearLayout; + +import com.melnykov.fab.FloatingActionButton; +import com.xabber.android.R; +import com.xabber.android.data.account.AccountManager; +import com.xabber.android.data.account.StatusMode; +import com.xabber.android.data.extension.avatar.AvatarManager; + +import java.util.ArrayList; +import java.util.Collections; + +import de.hdodenhof.circleimageview.CircleImageView; + + +public class AccountActionButtonsAdapter implements UpdatableAdapter { + + private final Activity activity; + + /** + * Listener for click on elements. + */ + private final View.OnClickListener onClickListener; + + /** + * Layout to be populated. + */ + private final LinearLayout linearLayout; + + /** + * List of accounts. + */ + private final ArrayList accounts; + private int[] accountActionBarColors; + private int[] accountStatusBarColors; + private int[] accountBackgroundColors; + + public AccountActionButtonsAdapter(Activity activity, + View.OnClickListener onClickListener, LinearLayout linearLayout) { + super(); + this.activity = activity; + this.onClickListener = onClickListener; + this.linearLayout = linearLayout; + accounts = new ArrayList<>(); + + Resources resources = activity.getResources(); + + accountActionBarColors = resources.getIntArray(R.array.account_action_bar); + accountStatusBarColors = resources.getIntArray(R.array.account_status_bar); + accountBackgroundColors = resources.getIntArray(R.array.account_background); + } + + /** + * Rebuild list of accounts. + *

+ * Call it on account creation, deletion, enable or disable. + */ + public void rebuild() { + accounts.clear(); + accounts.addAll(AccountManager.getInstance().getAccounts()); + + Collections.sort(accounts); + final int size = accounts.size(); + final LayoutInflater inflater = (LayoutInflater) activity + .getSystemService(Activity.LAYOUT_INFLATER_SERVICE); + + while (linearLayout.getChildCount() < size) { + View view = inflater.inflate(R.layout.account_action_button, linearLayout, false); + view.setOnClickListener(onClickListener); + linearLayout.addView(view); + } + + while (linearLayout.getChildCount() > size) { + linearLayout.removeViewAt(size); + } + onChange(); + } + + @Override + public void onChange() { + for (int index = 0; index < accounts.size(); index++) { + View view = linearLayout.getChildAt(index); + + final CircleImageView circleImageView = (CircleImageView) view.findViewById(R.id.account_avatar); + final String account = accounts.get(index); + circleImageView.setImageDrawable(AvatarManager.getInstance().getAccountAvatar(account)); + + FloatingActionButton backgroundActionButton = (FloatingActionButton) view.findViewById(R.id.fab); + int colorLevel = AccountManager.getInstance().getColorLevel(account); + backgroundActionButton.setColorNormal(accountActionBarColors[colorLevel]); + backgroundActionButton.setColorPressed(accountStatusBarColors[colorLevel]); + backgroundActionButton.setColorRipple(accountBackgroundColors[colorLevel]); + + String selectedAccount = AccountManager.getInstance().getSelectedAccount(); + + int shadowVisibility; + + if (selectedAccount == null) { + shadowVisibility = View.GONE; + } else { + shadowVisibility = View.VISIBLE; + if (selectedAccount.equalsIgnoreCase(account)) { + shadowVisibility = View.GONE; + } + } + + view.findViewById(R.id.account_unselected_shadow).setVisibility(shadowVisibility); + + StatusMode statusMode = AccountManager.getInstance().getAccount(account).getDisplayStatusMode(); + int offlineShadowVisibility; + if (statusMode == StatusMode.connection || statusMode == StatusMode.unavailable) { + offlineShadowVisibility = View.VISIBLE; + } else { + offlineShadowVisibility = View.GONE; + } + view.findViewById(R.id.account_offline_shadow).setVisibility(offlineShadowVisibility); + + } + } + + public int getCount() { + return accounts.size(); + } + + public Object getItem(int position) { + return accounts.get(position); + } + + public String getItemForView(View view) { + for (int index = 0; index < linearLayout.getChildCount(); index++) { + if (view == linearLayout.getChildAt(index)) { + return accounts.get(index); + } + } + return null; + } +} diff --git a/app/src/main/java/com/xabber/android/ui/adapter/AccountChooseAdapter.java b/app/src/main/java/com/xabber/android/ui/adapter/AccountChooseAdapter.java index c8c05686ab..f2041ef912 100644 --- a/app/src/main/java/com/xabber/android/ui/adapter/AccountChooseAdapter.java +++ b/app/src/main/java/com/xabber/android/ui/adapter/AccountChooseAdapter.java @@ -1,22 +1,19 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.ui.adapter; -import java.util.ArrayList; -import java.util.Collections; - import android.app.Activity; import android.view.View; import android.view.ViewGroup; @@ -24,80 +21,71 @@ import android.widget.ImageView; import android.widget.TextView; +import com.xabber.android.R; import com.xabber.android.data.account.AccountManager; import com.xabber.android.data.extension.avatar.AvatarManager; -import com.xabber.androiddev.R; + +import java.util.ArrayList; +import java.util.Collections; /** * Adapter for drop down list of accounts. - * + * * @author alexander.ivanov - * */ public class AccountChooseAdapter extends BaseAdapter { + protected final ArrayList accounts; + private final int[] accountColors; + private final Activity activity; + + public AccountChooseAdapter(Activity activity) { + super(); + this.activity = activity; + accounts = new ArrayList<>(AccountManager.getInstance().getAccounts()); + Collections.sort(accounts); + + accountColors = activity.getResources().getIntArray(R.array.account_action_bar); + } - private final Activity activity; - protected final ArrayList accounts; + @Override + public int getCount() { + return accounts.size(); + } - public AccountChooseAdapter(Activity activity) { - super(); - this.activity = activity; - accounts = new ArrayList(AccountManager.getInstance() - .getAccounts()); - Collections.sort(accounts); - } + @Override + public Object getItem(int position) { + return accounts.get(position); + } - @Override - public int getCount() { - return accounts.size(); - } + @Override + public long getItemId(int position) { + return position; + } - @Override - public Object getItem(int position) { - return accounts.get(position); - } + @Override + public View getView(int position, View convertView, ViewGroup parent) { + final View view; + final AccountManager accountManager = AccountManager.getInstance(); + if (convertView == null) { + view = activity.getLayoutInflater().inflate( + R.layout.account_choose_item, parent, false); + } else { + view = convertView; + } + final String account = (String) getItem(position); - @Override - public long getItemId(int position) { - return position; - } + int accountColor = accountColors[accountManager.getColorLevel(account)]; + ((ImageView) view.findViewById(R.id.avatar)) + .setImageDrawable(AvatarManager.getInstance().getAccountAvatar(account)); - @Override - public View getView(int position, View convertView, ViewGroup parent) { - final View view; - final AccountManager accountManager = AccountManager.getInstance(); - if (convertView == null) { - view = activity.getLayoutInflater().inflate( - R.layout.account_choose_item, parent, false); - } else { - view = convertView; - } - final String account = (String) getItem(position); - ((ImageView) view.findViewById(R.id.avatar)) - .setImageDrawable(AvatarManager.getInstance().getAccountAvatar( - account)); - ((TextView) view.findViewById(R.id.name)).setText(accountManager - .getVerboseName(account)); - return view; - } + ((TextView) view.findViewById(R.id.name)).setText(accountManager + .getVerboseName(account)); + return view; + } - @Override - public View getDropDownView(int position, View convertView, ViewGroup parent) { - final View view; - final AccountManager accountManager = AccountManager.getInstance(); - if (convertView == null) { - view = activity.getLayoutInflater().inflate( - R.layout.account_choose_dropdown, parent, false); - } else { - view = convertView; - } - final String account = (String) getItem(position); - ((ImageView) view.findViewById(R.id.avatar)) - .setImageDrawable(AvatarManager.getInstance().getAccountAvatar( - account)); - ((TextView) view.findViewById(R.id.name)).setText(accountManager - .getVerboseName(account)); - return view; - } + @Override + public View getDropDownView(int position, View convertView, ViewGroup parent) { + return getView(position, convertView, parent); + } } diff --git a/app/src/main/java/com/xabber/android/ui/adapter/AccountConfiguration.java b/app/src/main/java/com/xabber/android/ui/adapter/AccountConfiguration.java index 4b333a3587..b3342cda86 100644 --- a/app/src/main/java/com/xabber/android/ui/adapter/AccountConfiguration.java +++ b/app/src/main/java/com/xabber/android/ui/adapter/AccountConfiguration.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -26,43 +26,43 @@ */ public class AccountConfiguration extends GroupConfiguration { - private final TreeMap groups; + private final TreeMap groups; - public AccountConfiguration(String account, String user, - GroupStateProvider groupStateProvider) { - super(account, user, groupStateProvider); - groups = new TreeMap(); - } + public AccountConfiguration(String account, String user, + GroupStateProvider groupStateProvider) { + super(account, user, groupStateProvider); + groups = new TreeMap<>(); + } - /** - * Gets group by name. - * - * @param group - * @return null will be returns if there is no such group. - */ - public GroupConfiguration getGroupConfiguration(String group) { - return groups.get(group); - } + /** + * Gets group by name. + * + * @param group + * @return null will be returns if there is no such group. + */ + public GroupConfiguration getGroupConfiguration(String group) { + return groups.get(group); + } - /** - * Adds new group. - * - * @param groupConfiguration - */ - public void addGroupConfiguration(GroupConfiguration groupConfiguration) { - groups.put(groupConfiguration.getUser(), groupConfiguration); - } + /** + * Adds new group. + * + * @param groupConfiguration + */ + public void addGroupConfiguration(GroupConfiguration groupConfiguration) { + groups.put(groupConfiguration.getUser(), groupConfiguration); + } - /** - * Returns sorted list of groups. - * - * @return - */ - public Collection getSortedGroupConfigurations() { - ArrayList groups = new ArrayList( - this.groups.values()); - Collections.sort(groups); - return Collections.unmodifiableCollection(groups); - } + /** + * Returns sorted list of groups. + * + * @return + */ + public Collection getSortedGroupConfigurations() { + ArrayList groups = new ArrayList( + this.groups.values()); + Collections.sort(groups); + return Collections.unmodifiableCollection(groups); + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/ui/adapter/AccountListAdapter.java b/app/src/main/java/com/xabber/android/ui/adapter/AccountListAdapter.java index 7a4031ef07..ff200b4cbd 100644 --- a/app/src/main/java/com/xabber/android/ui/adapter/AccountListAdapter.java +++ b/app/src/main/java/com/xabber/android/ui/adapter/AccountListAdapter.java @@ -1,85 +1,87 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.ui.adapter; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - import android.app.Activity; +import android.graphics.drawable.ColorDrawable; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; +import com.xabber.android.R; import com.xabber.android.data.account.AccountItem; import com.xabber.android.data.account.AccountManager; import com.xabber.android.data.connection.ConnectionState; import com.xabber.android.data.extension.avatar.AvatarManager; -import com.xabber.android.ui.AccountList; -import com.xabber.androiddev.R; +import com.xabber.android.ui.preferences.AccountList; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; /** * Adapter for the list of accounts for {@link AccountList}. - * + * * @author alexander.ivanov - * */ public class AccountListAdapter extends BaseListEditorAdapter { + private final int[] accountColors; + + public AccountListAdapter(Activity activity) { + super(activity); + + accountColors = activity.getResources().getIntArray(R.array.account_action_bar); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View view; + AccountManager accountManager = AccountManager.getInstance(); + if (convertView == null) { + view = getActivity().getLayoutInflater().inflate(R.layout.account_list_item, parent, false); + } else { + view = convertView; + } + String account = getItem(position); - public AccountListAdapter(Activity activity) { - super(activity); - } + int accountColor = accountColors[accountManager.getColorLevel(account)]; - @Override - public View getView(int position, View convertView, ViewGroup parent) { - View view; - AccountManager accountManager = AccountManager.getInstance(); - if (convertView == null) { - view = getActivity().getLayoutInflater().inflate( - R.layout.account_list_item, parent, false); - } else { - view = convertView; - } - String account = getItem(position); + ((ImageView) view.findViewById(R.id.color)).setImageDrawable(new ColorDrawable(accountColor)); + ((ImageView) view.findViewById(R.id.avatar)) + .setImageDrawable(AvatarManager.getInstance().getAccountAvatar(account)); - ((ImageView) view.findViewById(R.id.color)) - .setImageLevel(accountManager.getColorLevel(account)); - ((ImageView) view.findViewById(R.id.avatar)) - .setImageDrawable(AvatarManager.getInstance().getAccountAvatar( - account)); - ((TextView) view.findViewById(R.id.name)).setText(accountManager - .getVerboseName(account)); - AccountItem accountItem = accountManager.getAccount(account); - ConnectionState state; - if (accountItem == null) - state = ConnectionState.offline; - else - state = accountItem.getState(); - ((TextView) view.findViewById(R.id.status)).setText(getActivity() - .getString(state.getStringId())); - return view; - } + ((TextView) view.findViewById(R.id.name)).setText(accountManager.getVerboseName(account)); + AccountItem accountItem = accountManager.getAccount(account); + ConnectionState state; + if (accountItem == null) { + state = ConnectionState.offline; + } else { + state = accountItem.getState(); + } + ((TextView) view.findViewById(R.id.status)).setText(getActivity().getString(state.getStringId())); + return view; + } - @Override - protected Collection getTags() { - List list = new ArrayList(); - list.addAll(AccountManager.getInstance().getAllAccounts()); - Collections.sort(list); - return list; - } + @Override + protected Collection getTags() { + List list = new ArrayList<>(); + list.addAll(AccountManager.getInstance().getAllAccounts()); + Collections.sort(list); + return list; + } } diff --git a/app/src/main/java/com/xabber/android/ui/adapter/AccountToggleAdapter.java b/app/src/main/java/com/xabber/android/ui/adapter/AccountToggleAdapter.java deleted file mode 100644 index 7edca92314..0000000000 --- a/app/src/main/java/com/xabber/android/ui/adapter/AccountToggleAdapter.java +++ /dev/null @@ -1,162 +0,0 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.xabber.android.ui.adapter; - -import java.util.ArrayList; -import java.util.Collections; - -import android.app.Activity; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.widget.ImageView; -import android.widget.LinearLayout; - -import com.xabber.android.data.SettingsManager; -import com.xabber.android.data.account.AccountManager; -import com.xabber.android.data.account.StatusMode; -import com.xabber.android.data.extension.avatar.AvatarManager; -import com.xabber.androiddev.R; - -/** - * Adapter for the list of accounts in the title of contact list. - * - * @author alexander.ivanov - * - */ -public class AccountToggleAdapter implements UpdatableAdapter { - - private final Activity activity; - - /** - * Listener for click on elements. - */ - private final OnClickListener onClickListener; - - /** - * Layout to be populated. - */ - private final LinearLayout linearLayout; - - /** - * List of accounts. - */ - private final ArrayList accounts; - - public AccountToggleAdapter(Activity activity, - OnClickListener onClickListener, LinearLayout linearLayout) { - super(); - this.activity = activity; - this.onClickListener = onClickListener; - this.linearLayout = linearLayout; - accounts = new ArrayList(); - } - - /** - * Rebuild list of accounts. - * - * Call it on account creation, deletion, enable or disable. - */ - public void rebuild() { - accounts.clear(); - accounts.addAll(AccountManager.getInstance().getAccounts()); - Collections.sort(accounts); - final int size = accounts.size(); - final LayoutInflater inflater = (LayoutInflater) activity - .getSystemService(Activity.LAYOUT_INFLATER_SERVICE); - while (linearLayout.getChildCount() < size) { - final View view = inflater.inflate(R.layout.account_toggler_item, - linearLayout, false); - linearLayout.addView(view); - final AccountViewHolder accountViewHolder = new AccountViewHolder( - view); - view.setTag(accountViewHolder); - activity.registerForContextMenu(accountViewHolder.statusMode); - accountViewHolder.statusMode.setOnClickListener(onClickListener); - } - while (linearLayout.getChildCount() > size) - linearLayout.removeViewAt(size); - onChange(); - } - - @Override - public void onChange() { - boolean contactsShowAccounts = SettingsManager.contactsShowAccounts(); - String selected = AccountManager.getInstance().getSelectedAccount(); - for (int index = 0; index < accounts.size(); index++) { - final View view = linearLayout.getChildAt(index); - final AccountViewHolder accountViewHolder = (AccountViewHolder) view - .getTag(); - final String account = accounts.get(index); - StatusMode statusMode = AccountManager.getInstance() - .getAccount(account).getDisplayStatusMode(); - int colorLevel = AccountManager.getInstance() - .getColorLevel(account); - view.getBackground().setLevel(colorLevel); - if (contactsShowAccounts) - accountViewHolder.statusMode - .setBackgroundResource(R.drawable.account_border); - else - accountViewHolder.statusMode - .setBackgroundResource(R.drawable.account_border_persistent); - if (selected == null || account.equals(selected)) - accountViewHolder.disabled.setVisibility(View.GONE); - else - accountViewHolder.disabled.setVisibility(View.VISIBLE); - accountViewHolder.statusMode.getBackground().setLevel(colorLevel); - accountViewHolder.statusMode.setImageLevel(statusMode.ordinal()); - accountViewHolder.avatar.setImageDrawable(AvatarManager - .getInstance().getAccountAvatar(account)); - } - } - - public int getCount() { - return accounts.size(); - } - - public Object getItem(int position) { - return accounts.get(position); - } - - /** - * Get the data item associated with the specified view. - * - * @param view - * direct child of linear layout or status_mode view in direct - * child. - * @return The data for the specified view. - */ - public Object getItemForView(View view) { - if (view.getId() == R.id.status_mode) - view = (View) view.getParent(); - for (int index = 0; index < linearLayout.getChildCount(); index++) - if (view == linearLayout.getChildAt(index)) - return accounts.get(index); - return null; - } - - private static class AccountViewHolder { - final ImageView statusMode; - final ImageView avatar; - final ImageView disabled; - - public AccountViewHolder(View view) { - statusMode = (ImageView) view.findViewById(R.id.status_mode); - avatar = (ImageView) view.findViewById(R.id.avatar); - disabled = (ImageView) view.findViewById(R.id.disabled); - } - } - -} diff --git a/app/src/main/java/com/xabber/android/ui/adapter/AccountTypeAdapter.java b/app/src/main/java/com/xabber/android/ui/adapter/AccountTypeAdapter.java index 73452e362b..847e64d929 100644 --- a/app/src/main/java/com/xabber/android/ui/adapter/AccountTypeAdapter.java +++ b/app/src/main/java/com/xabber/android/ui/adapter/AccountTypeAdapter.java @@ -1,21 +1,19 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.ui.adapter; -import java.util.List; - import android.app.Activity; import android.view.View; import android.view.ViewGroup; @@ -23,72 +21,60 @@ import android.widget.ImageView; import android.widget.TextView; +import com.xabber.android.R; import com.xabber.android.data.account.AccountManager; import com.xabber.android.data.account.AccountType; -import com.xabber.androiddev.R; + +import java.util.List; /** * Adapter for drop down list of account's types. - * + * * @author alexander.ivanov - * */ public class AccountTypeAdapter extends BaseAdapter { - private final Activity activity; - private final List accountTypes; + private final Activity activity; + private final List accountTypes; - public AccountTypeAdapter(Activity activity) { - super(); - this.activity = activity; - accountTypes = AccountManager.getInstance().getAccountTypes(); - } + public AccountTypeAdapter(Activity activity) { + super(); + this.activity = activity; + accountTypes = AccountManager.getInstance().getAccountTypes(); + } - @Override - public int getCount() { - return accountTypes.size(); - } + @Override + public int getCount() { + return accountTypes.size(); + } - @Override - public Object getItem(int position) { - return accountTypes.get(position); - } + @Override + public Object getItem(int position) { + return accountTypes.get(position); + } - @Override - public long getItemId(int position) { - return position; - } + @Override + public long getItemId(int position) { + return position; + } - @Override - public View getView(int position, View convertView, ViewGroup parent) { - final View view; - if (convertView == null) { - view = activity.getLayoutInflater().inflate( - R.layout.account_type_item, parent, false); - } else { - view = convertView; - } - final AccountType type = (AccountType) getItem(position); - ((ImageView) view.findViewById(R.id.avatar)).setImageDrawable(type - .getIcon()); - ((TextView) view.findViewById(R.id.name)).setText(type.getName()); - return view; - } + @Override + public View getView(int position, View convertView, ViewGroup parent) { + final View view; + if (convertView == null) { + view = activity.getLayoutInflater().inflate(R.layout.account_type_item, parent, false); + } else { + view = convertView; + } + final AccountType type = (AccountType) getItem(position); + ((ImageView) view.findViewById(R.id.avatar)).setImageDrawable(type.getIcon()); + ((TextView) view.findViewById(R.id.name)).setText(type.getName()); + return view; + } - @Override - public View getDropDownView(int position, View convertView, ViewGroup parent) { - final View view; - if (convertView == null) { - view = activity.getLayoutInflater().inflate( - R.layout.account_type_dropdown, parent, false); - } else { - view = convertView; - } - final AccountType type = (AccountType) getItem(position); - ((ImageView) view.findViewById(R.id.avatar)).setImageDrawable(type - .getIcon()); - ((TextView) view.findViewById(R.id.name)).setText(type.getName()); - return view; - } + @Override + public View getDropDownView(int position, View convertView, ViewGroup parent) { + return getView(position, convertView, parent); + } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/ui/adapter/BaseContactAdapter.java b/app/src/main/java/com/xabber/android/ui/adapter/BaseContactAdapter.java deleted file mode 100644 index 2ab6265d97..0000000000 --- a/app/src/main/java/com/xabber/android/ui/adapter/BaseContactAdapter.java +++ /dev/null @@ -1,139 +0,0 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.xabber.android.ui.adapter; - -import java.util.ArrayList; -import java.util.Locale; - -import android.app.Activity; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.Filter; -import android.widget.Filterable; - -import com.xabber.android.data.entity.BaseEntity; -import com.xabber.android.data.roster.AbstractContact; - -/** - * Base adapter for the list of contacts. - * - * @author alexander.ivanov - * - */ -public abstract class BaseContactAdapter - extends BaseAdapter implements UpdatableAdapter, Filterable { - - final Activity activity; - - final Locale locale; - - /** - * List of entities. - */ - final ArrayList baseEntities; - - /** - * Used view inflater. - */ - final Inflater inflater; - - /** - * Contact filter. - */ - ContactFilter contactFilter; - - /** - * Filter string. Can be null if filter is disabled. - */ - String filterString; - - public BaseContactAdapter(Activity activity, Inflater inflater) { - this.activity = activity; - this.locale = Locale.getDefault(); - this.baseEntities = new ArrayList(); - this.inflater = inflater; - inflater.setAdapter(this); - contactFilter = null; - filterString = null; - } - - /** - * @return View inflater. - */ - public Inflater getInflater() { - return inflater; - } - - @Override - public void onChange() { - notifyDataSetChanged(); - } - - @Override - public int getCount() { - return baseEntities.size(); - } - - @Override - public Object getItem(int position) { - return baseEntities.get(position); - } - - @Override - public long getItemId(int position) { - return position; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - final View view; - if (convertView == null) { - view = inflater.createView(position, parent); - view.setTag(inflater.createViewHolder(position, view)); - } else { - view = convertView; - } - inflater.getView(view, (AbstractContact) getItem(position)); - return view; - } - - @Override - public Filter getFilter() { - if (contactFilter == null) - contactFilter = new ContactFilter(); - return contactFilter; - } - - private class ContactFilter extends Filter { - - @Override - protected FilterResults performFiltering(CharSequence constraint) { - return null; - } - - @Override - protected void publishResults(CharSequence constraint, - FilterResults results) { - if (constraint == null || constraint.length() == 0) - filterString = null; - else - filterString = constraint.toString().toLowerCase(locale); - onChange(); - } - - } - -} diff --git a/app/src/main/java/com/xabber/android/ui/adapter/BaseContactInflater.java b/app/src/main/java/com/xabber/android/ui/adapter/BaseContactInflater.java deleted file mode 100644 index c70b25d403..0000000000 --- a/app/src/main/java/com/xabber/android/ui/adapter/BaseContactInflater.java +++ /dev/null @@ -1,185 +0,0 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.xabber.android.ui.adapter; - -import android.app.Activity; -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Shader.TileMode; -import android.graphics.drawable.BitmapDrawable; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.ImageView; -import android.widget.RelativeLayout; -import android.widget.TextView; - -import com.xabber.android.data.SettingsManager; -import com.xabber.android.data.roster.AbstractContact; -import com.xabber.android.ui.helper.AbstractAvatarInflaterHelper; -import com.xabber.androiddev.R; - -/** - * Provides views and fills them with data for {@link BaseContactAdapter}. - * - * @author alexander.ivanov - * - */ -public abstract class BaseContactInflater { - - final Activity activity; - - final LayoutInflater layoutInflater; - - final AbstractAvatarInflaterHelper avatarInflaterHelper; - - /** - * Repeated shadow for drawable. - */ - final BitmapDrawable shadowDrawable; - - /** - * Managed adapter. - */ - BaseAdapter adapter; - - public BaseContactInflater(Activity activity) { - this.activity = activity; - layoutInflater = (LayoutInflater) activity - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - avatarInflaterHelper = AbstractAvatarInflaterHelper - .createAbstractContactInflaterHelper(); - - Bitmap bitmap = BitmapFactory.decodeResource(activity.getResources(), - R.drawable.shadow); - shadowDrawable = new BitmapDrawable(bitmap); - shadowDrawable.setTileModeXY(TileMode.REPEAT, TileMode.REPEAT); - } - - /** - * Sets managed adapter. - * - * @param adapter - */ - void setAdapter(BaseAdapter adapter) { - this.adapter = adapter; - } - - /** - * Creates new view for specified position. - * - * @param position - * @param parent - * @return - */ - abstract View createView(int position, ViewGroup parent); - - /** - * Creates new instance of ViewHolder. - * - * @param position - * @param view - * @return - */ - abstract ViewHolder createViewHolder(int position, View view); - - /** - * Returns status text. - * - * @param abstractContact - * @return - */ - String getStatusText(AbstractContact abstractContact) { - return abstractContact.getStatusText(); - } - - /** - * Fills view for {@link BaseContactAdapter}. - * - * @param view - * view to be inflated. - * @param abstractContact - * contact to be shown. - */ - public void getView(View view, AbstractContact abstractContact) { - final ViewHolder viewHolder = (ViewHolder) view.getTag(); - if (abstractContact.isConnected()) - viewHolder.shadow.setVisibility(View.GONE); - else - viewHolder.shadow.setVisibility(View.VISIBLE); - - viewHolder.color.setImageLevel(abstractContact.getColorLevel()); - - if (SettingsManager.contactsShowAvatars()) { - viewHolder.avatar.setVisibility(View.VISIBLE); - viewHolder.avatar.setImageDrawable(abstractContact - .getAvatarForContactList()); - avatarInflaterHelper.updateAvatar(viewHolder.avatar, - abstractContact); - ((RelativeLayout.LayoutParams) viewHolder.panel.getLayoutParams()) - .addRule(RelativeLayout.RIGHT_OF, R.id.avatar); - } else { - viewHolder.avatar.setVisibility(View.GONE); - ((RelativeLayout.LayoutParams) viewHolder.panel.getLayoutParams()) - .addRule(RelativeLayout.RIGHT_OF, R.id.color); - } - - viewHolder.name.setText(abstractContact.getName()); - final String statusText = getStatusText(abstractContact); - if ("".equals(statusText)) { - viewHolder.name.getLayoutParams().height = activity.getResources() - .getDimensionPixelSize( - R.dimen.contact_name_height_hide_status); - viewHolder.name.setGravity(Gravity.CENTER_VERTICAL); - viewHolder.status.setVisibility(View.GONE); - } else { - viewHolder.name.getLayoutParams().height = activity.getResources() - .getDimensionPixelSize( - R.dimen.contact_name_height_show_status); - viewHolder.name.setGravity(Gravity.BOTTOM); - viewHolder.status.setText(statusText); - viewHolder.status.setVisibility(View.VISIBLE); - } - - viewHolder.shadow.setBackgroundDrawable(shadowDrawable); - } - - /** - * Holder for views in contact item. - */ - static class ViewHolder { - - final ImageView color; - final ImageView avatar; - final RelativeLayout panel; - final TextView name; - final TextView status; - final ImageView shadow; - - public ViewHolder(View view) { - color = (ImageView) view.findViewById(R.id.color); - avatar = (ImageView) view.findViewById(R.id.avatar); - panel = (RelativeLayout) view.findViewById(R.id.panel); - name = (TextView) view.findViewById(R.id.name); - status = (TextView) view.findViewById(R.id.status); - shadow = (ImageView) view.findViewById(R.id.shadow); - } - - } - -} diff --git a/app/src/main/java/com/xabber/android/ui/adapter/BaseListEditorAdapter.java b/app/src/main/java/com/xabber/android/ui/adapter/BaseListEditorAdapter.java index 023037c2dc..63a2585de0 100644 --- a/app/src/main/java/com/xabber/android/ui/adapter/BaseListEditorAdapter.java +++ b/app/src/main/java/com/xabber/android/ui/adapter/BaseListEditorAdapter.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -21,52 +21,51 @@ import android.app.Activity; import android.widget.BaseAdapter; -import com.xabber.android.ui.helper.BaseListEditor; +import com.xabber.android.ui.preferences.BaseListEditor; /** * This class manage abstract list for {@link BaseListEditor}. - * + * * @author alexander.ivanov - * */ public abstract class BaseListEditorAdapter extends BaseAdapter implements - UpdatableAdapter { + UpdatableAdapter { - private final Activity activity; - private final List tags; + private final Activity activity; + private final List tags; - public BaseListEditorAdapter(Activity activity) { - super(); - this.activity = activity; - this.tags = new ArrayList(); - } + public BaseListEditorAdapter(Activity activity) { + super(); + this.activity = activity; + this.tags = new ArrayList(); + } - protected Activity getActivity() { - return activity; - } + protected Activity getActivity() { + return activity; + } - @Override - public int getCount() { - return tags.size(); - } + @Override + public int getCount() { + return tags.size(); + } - @Override - public T getItem(int position) { - return tags.get(position); - } + @Override + public T getItem(int position) { + return tags.get(position); + } - @Override - public long getItemId(int position) { - return position; - } + @Override + public long getItemId(int position) { + return position; + } - @Override - public void onChange() { - tags.clear(); - tags.addAll(getTags()); - notifyDataSetChanged(); - } + @Override + public void onChange() { + tags.clear(); + tags.addAll(getTags()); + notifyDataSetChanged(); + } - protected abstract Collection getTags(); + protected abstract Collection getTags(); } diff --git a/app/src/main/java/com/xabber/android/ui/adapter/ChatComparator.java b/app/src/main/java/com/xabber/android/ui/adapter/ChatComparator.java index 02b659fefb..c34db4122e 100644 --- a/app/src/main/java/com/xabber/android/ui/adapter/ChatComparator.java +++ b/app/src/main/java/com/xabber/android/ui/adapter/ChatComparator.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,19 +20,19 @@ public class ChatComparator implements Comparator { - public static final ChatComparator CHAT_COMPARATOR = new ChatComparator(); + public static final ChatComparator CHAT_COMPARATOR = new ChatComparator(); - @Override - public int compare(AbstractChat object1, AbstractChat object2) { - if (object1.getLastTime() == null) { - if (object2.getLastTime() != null) - return 1; - return 0; - } else { - if (object2.getLastTime() == null) - return -1; - return -object1.getLastTime().compareTo(object2.getLastTime()); - } - } + @Override + public int compare(AbstractChat object1, AbstractChat object2) { + if (object1.getLastTime() == null) { + if (object2.getLastTime() != null) + return 1; + return 0; + } else { + if (object2.getLastTime() == null) + return -1; + return -object1.getLastTime().compareTo(object2.getLastTime()); + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/ui/adapter/ChatContactInflater.java b/app/src/main/java/com/xabber/android/ui/adapter/ChatContactInflater.java deleted file mode 100644 index 61f493092c..0000000000 --- a/app/src/main/java/com/xabber/android/ui/adapter/ChatContactInflater.java +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.xabber.android.ui.adapter; - -import android.app.Activity; -import android.content.res.TypedArray; -import android.view.View; - -import com.xabber.android.data.message.MessageManager; -import com.xabber.android.data.roster.AbstractContact; -import com.xabber.androiddev.R; - -/** - * Inflate view with contact's last message or status text as well as active - * chat badge. - * - * @author alexander.ivanov - * - */ -public class ChatContactInflater extends ClientContactInflater { - - /** - * Name's normal color. - */ - private final int textColorPrimary; - - /** - * Status's normal color. - */ - private final int textColorSecondary; - - public ChatContactInflater(Activity activity) { - super(activity); - TypedArray typedArray; - typedArray = activity.getTheme().obtainStyledAttributes( - new int[] { android.R.attr.textColorPrimary, - android.R.attr.textColorSecondary, }); - textColorPrimary = typedArray.getColor(0, 0); - textColorSecondary = typedArray.getColor(1, 0); - typedArray.recycle(); - } - - @Override - ViewHolder createViewHolder(int position, View view) { - return new ViewHolder(view); - } - - @Override - String getStatusText(AbstractContact abstractContact) { - if (MessageManager.getInstance().hasActiveChat( - abstractContact.getAccount(), abstractContact.getUser())) - return MessageManager.getInstance().getLastText( - abstractContact.getAccount(), abstractContact.getUser()); - else - return super.getStatusText(abstractContact); - } - - @Override - public void getView(View view, AbstractContact abstractContact) { - super.getView(view, abstractContact); - final ViewHolder contactViewHolder = (ViewHolder) view.getTag(); - if (MessageManager.getInstance().hasActiveChat( - abstractContact.getAccount(), abstractContact.getUser())) { - contactViewHolder.panel - .setBackgroundResource(R.drawable.active_chat); - contactViewHolder.name.setTextColor(activity.getResources() - .getColor(android.R.color.primary_text_light)); - contactViewHolder.status.setTextColor(activity.getResources() - .getColor(android.R.color.secondary_text_light)); - } else { - contactViewHolder.panel.setBackgroundDrawable(null); - contactViewHolder.name.setTextColor(textColorPrimary); - contactViewHolder.status.setTextColor(textColorSecondary); - } - } - - static class ViewHolder extends ClientContactInflater.ViewHolder { - - final View panel; - - public ViewHolder(View view) { - super(view); - panel = view.findViewById(R.id.panel); - } - - } - -} diff --git a/app/src/main/java/com/xabber/android/ui/adapter/ChatListAdapter.java b/app/src/main/java/com/xabber/android/ui/adapter/ChatListAdapter.java index 38413b522d..3438968b4d 100644 --- a/app/src/main/java/com/xabber/android/ui/adapter/ChatListAdapter.java +++ b/app/src/main/java/com/xabber/android/ui/adapter/ChatListAdapter.java @@ -1,120 +1,54 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ package com.xabber.android.ui.adapter; -import java.util.ArrayList; -import java.util.Collections; - -import android.app.Activity; +import android.content.Context; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; -import android.widget.ImageView; -import android.widget.TextView; -import android.widget.Toast; import com.xabber.android.data.message.AbstractChat; -import com.xabber.android.data.message.MessageManager; -import com.xabber.android.data.notification.NotificationManager; import com.xabber.android.data.roster.AbstractContact; import com.xabber.android.data.roster.RosterManager; -import com.xabber.android.ui.ChatList; -import com.xabber.android.ui.helper.AbstractAvatarInflaterHelper; -import com.xabber.androiddev.R; - -/** - * Adapter for {@link ChatList}. - * - * @author alexander.ivanov - * - */ -public class ChatListAdapter extends BaseAdapter implements UpdatableAdapter { - - private final Activity activity; - - private final ArrayList abstractChats; - - private final AbstractAvatarInflaterHelper avatarInflaterHelper; - - public ChatListAdapter(Activity activity) { - this.activity = activity; - abstractChats = new ArrayList(); - avatarInflaterHelper = AbstractAvatarInflaterHelper - .createAbstractContactInflaterHelper(); - } - - @Override - public void onChange() { - abstractChats.clear(); - abstractChats.addAll(MessageManager.getInstance().getActiveChats()); - Collections.sort(abstractChats, ChatComparator.CHAT_COMPARATOR); - if (abstractChats.size() == 0) { - Toast.makeText(activity, R.string.chat_list_is_empty, - Toast.LENGTH_LONG).show(); - activity.finish(); - return; - } - notifyDataSetChanged(); - } - - @Override - public int getCount() { - return abstractChats.size(); - } - - @Override - public Object getItem(int position) { - return abstractChats.get(position); - } - - @Override - public long getItemId(int position) { - return position; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - final View view; - if (convertView == null) { - view = activity.getLayoutInflater().inflate( - R.layout.chat_list_item, parent, false); - } else { - view = convertView; - } - final AbstractChat abstractChat = (AbstractChat) getItem(position); - final AbstractContact abstractContact = RosterManager.getInstance() - .getBestContact(abstractChat.getAccount(), - abstractChat.getUser()); - final ImageView colorView = (ImageView) view.findViewById(R.id.color); - final ImageView avatarView = (ImageView) view.findViewById(R.id.avatar); - final TextView nameView = (TextView) view.findViewById(R.id.name); - final TextView textView = (TextView) view.findViewById(R.id.text); - colorView.setImageLevel(abstractContact.getColorLevel()); - avatarView.setImageDrawable(abstractContact.getAvatar()); - avatarInflaterHelper.updateAvatar(avatarView, abstractContact); - nameView.setText(abstractContact.getName()); - String statusText = MessageManager.getInstance().getLastText( - abstractContact.getAccount(), abstractContact.getUser()); - textView.setText(statusText); - boolean newMessages = NotificationManager.getInstance() - .getNotificationMessageCount(abstractChat.getAccount(), - abstractChat.getUser()) > 0; - textView.setTextAppearance(activity, - newMessages ? R.style.ChatList_Notification - : R.style.ChatList_Normal); - return view; - } +import java.util.ArrayList; +import java.util.List; + +public class ChatListAdapter extends BaseAdapter { + + private List chats; + + private final ContactItemInflater contactItemInflater; + + public ChatListAdapter(Context context) { + chats = new ArrayList<>(); + contactItemInflater = new ContactItemInflater(context); + } + + public void updateChats(List chats) { + this.chats.clear(); + this.chats.addAll(chats); + notifyDataSetChanged(); + } + + @Override + public int getCount() { + return chats.size(); + } + + @Override + public Object getItem(int position) { + return chats.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + final AbstractChat abstractChat = (AbstractChat) getItem(position); + final AbstractContact abstractContact = RosterManager.getInstance() + .getBestContact(abstractChat.getAccount(), abstractChat.getUser()); + return contactItemInflater.setUpContactView(convertView, parent, abstractContact); + } } diff --git a/app/src/main/java/com/xabber/android/ui/adapter/ChatMessageAdapter.java b/app/src/main/java/com/xabber/android/ui/adapter/ChatMessageAdapter.java index ab39d1c9b5..efc296d1a3 100644 --- a/app/src/main/java/com/xabber/android/ui/adapter/ChatMessageAdapter.java +++ b/app/src/main/java/com/xabber/android/ui/adapter/ChatMessageAdapter.java @@ -1,40 +1,30 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.ui.adapter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.List; - -import android.app.Activity; -import android.text.Spannable; -import android.text.SpannableStringBuilder; -import android.text.method.LinkMovementMethod; -import android.text.style.CharacterStyle; -import android.text.style.ImageSpan; -import android.text.style.TextAppearanceSpan; +import android.content.Context; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.BaseAdapter; import android.widget.ImageView; -import android.widget.RelativeLayout; import android.widget.TextView; +import com.xabber.android.R; +import com.xabber.android.data.LogManager; import com.xabber.android.data.SettingsManager; -import com.xabber.android.data.SettingsManager.ChatsDivide; import com.xabber.android.data.account.AccountItem; import com.xabber.android.data.account.AccountManager; import com.xabber.android.data.extension.avatar.AvatarManager; @@ -45,315 +35,319 @@ import com.xabber.android.data.message.MessageManager; import com.xabber.android.data.roster.AbstractContact; import com.xabber.android.data.roster.RosterManager; -import com.xabber.android.utils.Emoticons; import com.xabber.android.utils.StringUtils; -import com.xabber.androiddev.R; -/** - * Adapter for the list of messages in the chat. - * - * @author alexander.ivanov - * - */ -public class ChatMessageAdapter extends BaseAdapter implements UpdatableAdapter { - - private static final int TYPE_MESSAGE = 0; - private static final int TYPE_HINT = 1; - private static final int TYPE_EMPTY = 2; - - private final Activity activity; - private String account; - private String user; - private boolean isMUC; - private List messages; - - /** - * Message font appearance. - */ - private final int appearanceStyle; - - /** - * Divider between header and body. - */ - private final String divider; - - /** - * Text with extra information. - */ - private String hint; - - public ChatMessageAdapter(Activity activity) { - this.activity = activity; - messages = Collections.emptyList(); - account = null; - user = null; - hint = null; - appearanceStyle = SettingsManager.chatsAppearanceStyle(); - ChatsDivide chatsDivide = SettingsManager.chatsDivide(); - if (chatsDivide == ChatsDivide.always - || (chatsDivide == ChatsDivide.portial && !activity - .getResources().getBoolean(R.bool.landscape))) - divider = "\n"; - else - divider = " "; - } - - @Override - public int getCount() { - return messages.size() + 1; - } - - @Override - public Object getItem(int position) { - if (position < messages.size()) - return messages.get(position); - else - return null; - } - - @Override - public long getItemId(int position) { - return position; - } - - @Override - public int getViewTypeCount() { - return 3; - } - - @Override - public int getItemViewType(int position) { - if (position < messages.size()) - return TYPE_MESSAGE; - else - return hint == null ? TYPE_EMPTY : TYPE_HINT; - } - - private void append(SpannableStringBuilder builder, CharSequence text, - CharacterStyle span) { - int start = builder.length(); - builder.append(text); - builder.setSpan(span, start, start + text.length(), - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - final int type = getItemViewType(position); - final View view; - if (convertView == null) { - final int resource; - if (type == TYPE_MESSAGE) - resource = R.layout.chat_viewer_message; - else if (type == TYPE_HINT) - resource = R.layout.chat_viewer_info; - else if (type == TYPE_EMPTY) - resource = R.layout.chat_viewer_empty; - else - throw new IllegalStateException(); - view = activity.getLayoutInflater() - .inflate(resource, parent, false); - if (type == TYPE_MESSAGE) - ((TextView) view.findViewById(R.id.text)).setTextAppearance( - activity, appearanceStyle); - } else - view = convertView; - - if (type == TYPE_EMPTY) - return view; - - if (type == TYPE_HINT) { - TextView textView = ((TextView) view.findViewById(R.id.info)); - textView.setText(hint); - textView.setTextAppearance(activity, R.style.ChatInfo_Warning); - return view; - } - - final MessageItem messageItem = (MessageItem) getItem(position); - final String name; - final String account = messageItem.getChat().getAccount(); - final String user = messageItem.getChat().getUser(); - final String resource = messageItem.getResource(); - final boolean incoming = messageItem.isIncoming(); - if (isMUC) { - name = resource; - } else { - if (incoming) - name = RosterManager.getInstance().getName(account, user); - else - name = AccountManager.getInstance().getNickName(account); - } - if (incoming) { - if (view.getBackground() == null) - view.setBackgroundResource(R.drawable.chat_bg); - view.getBackground().setLevel( - AccountManager.getInstance().getColorLevel(account)); - } else { - view.setBackgroundDrawable(null); - } - Spannable text = messageItem.getSpannable(); - TextView textView = (TextView) view.findViewById(R.id.text); - ImageView avatarView = (ImageView) view.findViewById(R.id.avatar); - ChatAction action = messageItem.getAction(); - String time = StringUtils.getSmartTimeText(messageItem.getTimestamp()); - SpannableStringBuilder builder = new SpannableStringBuilder(); - if (action == null) { - int messageResource = R.drawable.ic_message_delivered; - if (!incoming) { - if (messageItem.isError()) - messageResource = R.drawable.ic_message_has_error; - else if (!messageItem.isSent()) - messageResource = R.drawable.ic_message_not_sent; - else if (!messageItem.isDelivered()) - messageResource = R.drawable.ic_message_not_delivered; - } - append(builder, " ", new ImageSpan(activity, messageResource)); - append(builder, " ", new TextAppearanceSpan(activity, - R.style.ChatHeader)); - append(builder, time, new TextAppearanceSpan(activity, - R.style.ChatHeader_Time)); - append(builder, " ", new TextAppearanceSpan(activity, - R.style.ChatHeader)); - append(builder, name, new TextAppearanceSpan(activity, - R.style.ChatHeader_Name)); - append(builder, divider, new TextAppearanceSpan(activity, - R.style.ChatHeader)); - Date timeStamp = messageItem.getDelayTimestamp(); - if (timeStamp != null) { - String delay = activity.getString( - incoming ? R.string.chat_delay : R.string.chat_typed, - StringUtils.getSmartTimeText(timeStamp)); - append(builder, delay, new TextAppearanceSpan(activity, - R.style.ChatHeader_Delay)); - append(builder, divider, new TextAppearanceSpan(activity, - R.style.ChatHeader)); - } - if (messageItem.isUnencypted()) { - append(builder, - activity.getString(R.string.otr_unencrypted_message), - new TextAppearanceSpan(activity, - R.style.ChatHeader_Delay)); - append(builder, divider, new TextAppearanceSpan(activity, - R.style.ChatHeader)); - } - Emoticons.getSmiledText(activity.getApplication(), text); - if (messageItem.getTag() == null) - builder.append(text); - else - append(builder, text, new TextAppearanceSpan(activity, - R.style.ChatRead)); - } else { - append(builder, time, new TextAppearanceSpan(activity, - R.style.ChatHeader_Time)); - append(builder, " ", new TextAppearanceSpan(activity, - R.style.ChatHeader)); - text = Emoticons.newSpannable(action.getText(activity, name, - text.toString())); - Emoticons.getSmiledText(activity.getApplication(), text); - append(builder, text, new TextAppearanceSpan(activity, - R.style.ChatHeader_Delay)); - } - textView.setText(builder); - textView.setMovementMethod(LinkMovementMethod.getInstance()); - if (SettingsManager.chatsShowAvatars()) { - avatarView.setVisibility(View.VISIBLE); - if (!incoming - || (isMUC && MUCManager.getInstance() - .getNickname(account, user) - .equalsIgnoreCase(resource))) { - avatarView.setImageDrawable(AvatarManager.getInstance() - .getAccountAvatar(account)); - } else { - if (isMUC) { - if ("".equals(resource)) { - avatarView.setImageDrawable(AvatarManager.getInstance() - .getRoomAvatar(user)); - } else { - avatarView.setImageDrawable(AvatarManager.getInstance() - .getOccupantAvatar(user + "/" + resource)); - } - } else { - avatarView.setImageDrawable(AvatarManager.getInstance() - .getUserAvatar(user)); - } - } - ((RelativeLayout.LayoutParams) textView.getLayoutParams()).addRule( - RelativeLayout.RIGHT_OF, R.id.avatar); - } else { - avatarView.setVisibility(View.GONE); - ((RelativeLayout.LayoutParams) textView.getLayoutParams()).addRule( - RelativeLayout.RIGHT_OF, 0); - } - return view; - } - - public String getAccount() { - return account; - } - - public String getUser() { - return user; - } - - /** - * Changes managed chat. - * - * @param account - * @param user - */ - public void setChat(String account, String user) { - this.account = account; - this.user = user; - this.isMUC = MUCManager.getInstance().hasRoom(account, user); - onChange(); - } - - @Override - public void onChange() { - messages = new ArrayList(MessageManager.getInstance() - .getMessages(account, user)); - hint = getHint(); - notifyDataSetChanged(); - } - - /** - * @return New hint. - */ - private String getHint() { - AccountItem accountItem = AccountManager.getInstance().getAccount( - account); - boolean online; - if (accountItem == null) - online = false; - else - online = accountItem.getState().isConnected(); - final AbstractContact abstractContact = RosterManager.getInstance() - .getBestContact(account, user); - if (!online) { - if (abstractContact instanceof RoomContact) - return activity.getString(R.string.muc_is_unavailable); - else - return activity.getString(R.string.account_is_offline); - } else if (!abstractContact.getStatusMode().isOnline()) { - if (abstractContact instanceof RoomContact) - return activity.getString(R.string.muc_is_unavailable); - else - return activity.getString(R.string.contact_is_offline, - abstractContact.getName()); - } - return null; - } - - /** - * Contact information has been changed. Renews hint and updates data if - * necessary. - */ - public void updateInfo() { - String info = getHint(); - if (this.hint == info || (this.hint != null && this.hint.equals(info))) - return; - this.hint = info; - notifyDataSetChanged(); - } +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +public class ChatMessageAdapter extends RecyclerView.Adapter implements UpdatableAdapter { + + public static final int VIEW_TYPE_INCOMING_MESSAGE = 2; + public static final int VIEW_TYPE_OUTGOING_MESSAGE = 3; + private static final int VIEW_TYPE_HINT = 1; + private static final int VIEW_TYPE_ACTION_MESSAGE = 4; + + private final Context context; + private final Message.MessageClickListener messageClickListener; + /** + * Message font appearance. + */ + private final int appearanceStyle; + private String account; + private String user; + private boolean isMUC; + private List messages; + /** + * Text with extra information. + */ + private String hint; + + public ChatMessageAdapter(Context context, String account, String user, Message.MessageClickListener messageClickListener) { + this.context = context; + messages = Collections.emptyList(); + this.account = account; + this.user = user; + this.messageClickListener = messageClickListener; + + isMUC = MUCManager.getInstance().hasRoom(account, user); + hint = null; + appearanceStyle = SettingsManager.chatsAppearanceStyle(); + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + switch (viewType) { + case VIEW_TYPE_HINT: + return new BasicMessage(LayoutInflater.from(parent.getContext()) + .inflate(R.layout.chat_viewer_info, parent, false)); + + case VIEW_TYPE_ACTION_MESSAGE: + return new BasicMessage(LayoutInflater.from(parent.getContext()) + .inflate(R.layout.chat_viewer_action_message, parent, false)); + + case VIEW_TYPE_INCOMING_MESSAGE: + return new IncomingMessage(LayoutInflater.from(parent.getContext()) + .inflate(R.layout.chat_viewer_incoming_message, parent, false), messageClickListener); + + case VIEW_TYPE_OUTGOING_MESSAGE: + return new OutgoingMessage(LayoutInflater.from(parent.getContext()) + .inflate(R.layout.chat_viewer_outgoing_message, parent, false), messageClickListener); + default: + return null; + } + + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + final int viewType = getItemViewType(position); + + MessageItem messageItem = getMessageItem(position); + + switch (viewType) { + case VIEW_TYPE_HINT: + ((BasicMessage) holder).messageText.setText(hint); + break; + + case VIEW_TYPE_ACTION_MESSAGE: + ChatAction action = messageItem.getAction(); + String time = StringUtils.getSmartTimeText(context, messageItem.getTimestamp()); + String name = RosterManager.getInstance().getBestContact(account, messageItem.getChat().getUser()).getName(); + ((BasicMessage)holder).messageText.setText(time + ": " + + action.getText(context, name, messageItem.getSpannable().toString())); + + break; + + case VIEW_TYPE_INCOMING_MESSAGE: + setUpIncomingMessage((IncomingMessage) holder, messageItem); + break; + case VIEW_TYPE_OUTGOING_MESSAGE: + setUpMessage(messageItem, (Message) holder); + setStatusIcon(messageItem, (OutgoingMessage) holder); + break; + } + + } + + private void setUpIncomingMessage(IncomingMessage incomingMessage, MessageItem messageItem) { + setUpMessage(messageItem, incomingMessage); + + setUpAvatar(messageItem, incomingMessage); + + if (messageItem.getText().trim().isEmpty()) { + incomingMessage.messageBalloon.setVisibility(View.GONE); + incomingMessage.messageTime.setVisibility(View.GONE); + incomingMessage.avatar.setVisibility(View.GONE); + LogManager.w(this, "Empty message! Hidden, but need to correct"); + } else { + incomingMessage.messageBalloon.setVisibility(View.VISIBLE); + incomingMessage.messageTime.setVisibility(View.VISIBLE); + } + + } + + @Override + public int getItemCount() { + if (hint == null) { + return messages.size(); + } else { + return messages.size() + 1; + } + } + + public MessageItem getMessageItem(int position) { + if (position < messages.size()) { + return messages.get(position); + } else { + return null; + } + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public int getItemViewType(int position) { + if (position >= messages.size()) { + return VIEW_TYPE_HINT; + } + + MessageItem messageItem = getMessageItem(position); + if (messageItem.getAction() != null) { + return VIEW_TYPE_ACTION_MESSAGE; + } + + return messageItem.isIncoming() ? VIEW_TYPE_INCOMING_MESSAGE : VIEW_TYPE_OUTGOING_MESSAGE; + } + + private void setUpMessage(MessageItem messageItem, Message message) { + + if (isMUC) { + message.messageHeader.setText(messageItem.getResource()); + message.messageHeader.setVisibility(View.VISIBLE); + } else { + message.messageHeader.setVisibility(View.GONE); + } + + if (messageItem.isUnencypted()) { + message.messageUnencrypted.setVisibility(View.VISIBLE); + } else { + message.messageUnencrypted.setVisibility(View.GONE); + } + + message.messageText.setTextAppearance(context, appearanceStyle); + + message.messageText.setText(messageItem.getSpannable()); + + message.messageBalloon.getBackground().setLevel(AccountManager.getInstance().getColorLevel(account)); + + String time = StringUtils.getSmartTimeText(context, messageItem.getTimestamp()); + + Date delayTimestamp = messageItem.getDelayTimestamp(); + if (delayTimestamp != null) { + String delay = context.getString(messageItem.isIncoming() ? R.string.chat_delay : R.string.chat_typed, + StringUtils.getSmartTimeText(context, delayTimestamp)); + time += " (" + delay + ")"; + } + + message.messageTime.setText(time); + } + + private void setStatusIcon(MessageItem messageItem, OutgoingMessage message) { + message.statusIcon.setVisibility(View.VISIBLE); + + int messageIcon = R.drawable.ic_message_delivered_18dp; + if (messageItem.isError()) { + messageIcon = R.drawable.ic_message_has_error_18dp; + } else if (!messageItem.isSent()) { + messageIcon = R.drawable.ic_message_not_sent_18dp; + } else if (!messageItem.isDelivered()) { + message.statusIcon.setVisibility(View.INVISIBLE); + } + + message.statusIcon.setImageResource(messageIcon); + } + + private void setUpAvatar(MessageItem messageItem, IncomingMessage message) { + if (SettingsManager.chatsShowAvatars()) { + final String account = messageItem.getChat().getAccount(); + final String user = messageItem.getChat().getUser(); + final String resource = messageItem.getResource(); + + message.avatar.setVisibility(View.VISIBLE); + if ((isMUC && MUCManager.getInstance().getNickname(account, user).equalsIgnoreCase(resource))) { + message.avatar.setImageDrawable(AvatarManager.getInstance().getAccountAvatar(account)); + } else { + if (isMUC) { + if ("".equals(resource)) { + message.avatar.setImageDrawable(AvatarManager.getInstance().getRoomAvatar(user)); + } else { + message.avatar.setImageDrawable(AvatarManager.getInstance().getOccupantAvatar(user + "/" + resource)); + } + } else { + message.avatar.setImageDrawable(AvatarManager.getInstance().getUserAvatar(user)); + } + } + } else { + message.avatar.setVisibility(View.GONE); + } + } + + @Override + public void onChange() { + messages = new ArrayList<>(MessageManager.getInstance().getMessages(account, user)); + hint = getHint(); + notifyDataSetChanged(); + } + + /** + * @return New hint. + */ + private String getHint() { + AccountItem accountItem = AccountManager.getInstance().getAccount(account); + boolean online = accountItem != null && accountItem.getState().isConnected(); + final AbstractContact abstractContact = RosterManager.getInstance().getBestContact(account, user); + if (!online) { + if (abstractContact instanceof RoomContact) { + return context.getString(R.string.muc_is_unavailable); + } else { + return context.getString(R.string.account_is_offline); + } + } else if (!abstractContact.getStatusMode().isOnline()) { + if (abstractContact instanceof RoomContact) { + return context.getString(R.string.muc_is_unavailable); + } else { + return context.getString(R.string.contact_is_offline, abstractContact.getName()); + } + } + return null; + } + + public static class BasicMessage extends RecyclerView.ViewHolder { + + public TextView messageText; + + public BasicMessage(View itemView) { + super(itemView); + + messageText = (TextView) itemView.findViewById(R.id.message_text); + } + } + + public static abstract class Message extends BasicMessage implements View.OnClickListener { + + public TextView messageTime; + public TextView messageHeader; + public TextView messageUnencrypted; + public View messageBalloon; + + MessageClickListener onClickListener; + + public Message(View itemView, MessageClickListener onClickListener) { + super(itemView); + this.onClickListener = onClickListener; + + + messageTime = (TextView) itemView.findViewById(R.id.message_time); + messageHeader = (TextView) itemView.findViewById(R.id.message_header); + messageUnencrypted = (TextView) itemView.findViewById(R.id.message_unencrypted); + messageBalloon = itemView.findViewById(R.id.message_balloon); + + itemView.setOnClickListener(this); + } + + @Override + public void onClick(View v) { + onClickListener.onMessageClick(messageBalloon, getPosition()); + } + + public interface MessageClickListener { + void onMessageClick(View caller, int position); + } + + } + + public static class IncomingMessage extends Message { + + public ImageView avatar; + + public IncomingMessage(View itemView, MessageClickListener listener) { + super(itemView, listener); + avatar = (ImageView) itemView.findViewById(R.id.avatar); + } + } + + public static class OutgoingMessage extends Message { + + public ImageView statusIcon; + public OutgoingMessage(View itemView, MessageClickListener listener) { + super(itemView, listener); + statusIcon = (ImageView) itemView.findViewById(R.id.message_status_icon); + } + } } diff --git a/app/src/main/java/com/xabber/android/ui/adapter/ChatScrollIndicatorAdapter.java b/app/src/main/java/com/xabber/android/ui/adapter/ChatScrollIndicatorAdapter.java new file mode 100644 index 0000000000..25d4186a92 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/adapter/ChatScrollIndicatorAdapter.java @@ -0,0 +1,82 @@ +package com.xabber.android.ui.adapter; + +import android.app.Activity; +import android.graphics.drawable.ColorDrawable; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageView; +import android.widget.LinearLayout; + +import com.xabber.android.R; +import com.xabber.android.data.account.AccountManager; +import com.xabber.android.data.message.AbstractChat; +import com.xabber.android.ui.helper.AccountPainter; + +import java.util.ArrayList; + +public class ChatScrollIndicatorAdapter { + + private final Activity activity; + private final LinearLayout linearLayout; + private final AccountPainter accountPainter; + + public ChatScrollIndicatorAdapter(Activity activity, LinearLayout linearLayout) { + this.activity = activity; + this.linearLayout = linearLayout; + accountPainter = new AccountPainter(activity); + } + + public void select(int selectedPosition) { + for (int index = 0; index < linearLayout.getChildCount(); index++) { + final View view = linearLayout.getChildAt(index); + final AccountViewHolder accountViewHolder = (AccountViewHolder) view.getTag(); + + accountViewHolder.selection.setVisibility(index == selectedPosition ? View.VISIBLE : View.INVISIBLE); + accountViewHolder.body.setVisibility(index == selectedPosition ? View.INVISIBLE : View.VISIBLE); + } + } + + public void update(ArrayList activeChats) { + final LayoutInflater inflater + = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE); + + final int size = activeChats.size() + 1; + + linearLayout.removeAllViews(); + + for (int i = 0; i < size; i++) { + View view; + if (i == 0) { + view = inflater.inflate(R.layout.chat_scroll_indicator_item_square, linearLayout, false); + } else { + view = inflater.inflate(R.layout.chat_scroll_indicator_item_circle, linearLayout, false); + } + + + + linearLayout.addView(view); + final AccountViewHolder accountViewHolder = new AccountViewHolder(view); + + if (i > 0) { + int colorLevel = AccountManager.getInstance().getColorLevel(activeChats.get(i - 1).getAccount()); + accountViewHolder.body.setImageLevel(colorLevel); + accountViewHolder.selection.setImageLevel(colorLevel); + } else { + accountViewHolder.body.setImageDrawable(new ColorDrawable(accountPainter.getDefaultMainColor())); + accountViewHolder.selection.setImageDrawable(new ColorDrawable(accountPainter.getDefaultMainColor())); + } + + view.setTag(accountViewHolder); + } + } + + private static class AccountViewHolder { + final ImageView body; + final ImageView selection; + + public AccountViewHolder(View view) { + body = (ImageView) view.findViewById(R.id.indicator_item_body); + selection = (ImageView) view.findViewById(R.id.indicator_item_selection); + } + } +} diff --git a/app/src/main/java/com/xabber/android/ui/adapter/ChatViewerAdapter.java b/app/src/main/java/com/xabber/android/ui/adapter/ChatViewerAdapter.java index 5e286c31a0..a7cf4c085a 100644 --- a/app/src/main/java/com/xabber/android/ui/adapter/ChatViewerAdapter.java +++ b/app/src/main/java/com/xabber/android/ui/adapter/ChatViewerAdapter.java @@ -1,344 +1,196 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ package com.xabber.android.ui.adapter; -import java.util.ArrayList; -import java.util.Collection; - -import android.app.Activity; -import android.text.Editable; -import android.text.TextWatcher; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.View.OnCreateContextMenuListener; -import android.view.View.OnKeyListener; + +import android.app.Fragment; +import android.app.FragmentManager; +import android.support.v13.app.FragmentStatePagerAdapter; import android.view.ViewGroup; -import android.view.animation.Animation; -import android.view.animation.AnimationUtils; -import android.widget.BaseAdapter; -import android.widget.EditText; -import android.widget.ImageView; -import android.widget.ListView; -import android.widget.TextView; -import android.widget.TextView.OnEditorActionListener; - -import com.xabber.android.data.LogManager; -import com.xabber.android.data.SettingsManager; -import com.xabber.android.data.SettingsManager.SecurityOtrMode; + import com.xabber.android.data.entity.BaseEntity; -import com.xabber.android.data.extension.otr.OTRManager; -import com.xabber.android.data.extension.otr.SecurityLevel; import com.xabber.android.data.message.AbstractChat; import com.xabber.android.data.message.MessageManager; -import com.xabber.android.data.message.chat.ChatManager; -import com.xabber.android.data.roster.AbstractContact; -import com.xabber.android.data.roster.RosterManager; -import com.xabber.android.ui.helper.AbstractAvatarInflaterHelper; -import com.xabber.android.ui.helper.ContactTitleInflater; -import com.xabber.android.ui.widget.PageSwitcher; -import com.xabber.androiddev.R; +import com.xabber.android.ui.ChatViewerFragment; +import com.xabber.android.ui.RecentChatFragment; import com.xabber.xmpp.address.Jid; -/** - * Adapter for the list of chat pages. - * - * @author alexander.ivanov - * - */ -public class ChatViewerAdapter extends BaseAdapter implements SaveStateAdapter, - UpdatableAdapter { - - private final Activity activity; - - /** - * Intent sent while opening chat activity. - */ - private final AbstractChat intent; - - /** - * Position to insert intent. - */ - private final int intentPosition; - - private ArrayList activeChats; - - /** - * Listener for click on title bar and send button. - */ - private OnClickListener onClickListener; - - /** - * Listener for key press in edit view. - */ - private OnKeyListener onKeyListener; - - /** - * Listener for actions in edit view. - */ - private OnEditorActionListener onEditorActionListener; - - /** - * Listener for context menu in message list. - */ - private OnCreateContextMenuListener onCreateContextMenuListener; - - /** - * Listen for text to be changed. - */ - private OnTextChangedListener onTextChangedListener; - - private final AbstractAvatarInflaterHelper avatarInflaterHelper; - - private final Animation shake; - - public ChatViewerAdapter(Activity activity, String account, String user) { - this.activity = activity; - avatarInflaterHelper = AbstractAvatarInflaterHelper - .createAbstractContactInflaterHelper(); - activeChats = new ArrayList(); - intent = MessageManager.getInstance().getOrCreateChat(account, - Jid.getBareAddress(user)); - Collection activeChats = MessageManager - .getInstance().getActiveChats(); - if (activeChats.contains(intent)) - intentPosition = -1; - else - intentPosition = activeChats.size(); - onClickListener = null; - onKeyListener = null; - onEditorActionListener = null; - onCreateContextMenuListener = null; - onTextChangedListener = null; - shake = AnimationUtils.loadAnimation(activity, R.anim.shake); - onChange(); - } - - public OnClickListener getOnClickListener() { - return onClickListener; - } - - public void setOnClickListener(OnClickListener onClickListener) { - this.onClickListener = onClickListener; - } - - public OnKeyListener getOnKeyListener() { - return onKeyListener; - } - - public void setOnKeyListener(OnKeyListener onKeyListener) { - this.onKeyListener = onKeyListener; - } - - public OnEditorActionListener getOnEditorActionListener() { - return onEditorActionListener; - } - - public void setOnEditorActionListener( - OnEditorActionListener onEditorActionListener) { - this.onEditorActionListener = onEditorActionListener; - } - - public OnCreateContextMenuListener getOnCreateContextMenuListener() { - return onCreateContextMenuListener; - } - - public void setOnCreateContextMenuListener( - OnCreateContextMenuListener onCreateContextMenuListener) { - this.onCreateContextMenuListener = onCreateContextMenuListener; - } - - public OnTextChangedListener getOnTextChangedListener() { - return onTextChangedListener; - } - - public void setOnTextChangedListener( - OnTextChangedListener onTextChangedListener) { - this.onTextChangedListener = onTextChangedListener; - } - - @Override - public int getCount() { - return activeChats.size(); - } - - @Override - public Object getItem(int position) { - return activeChats.get(position); - } - - @Override - public long getItemId(int position) { - return position; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - View view; - final AbstractChat chat = (AbstractChat) getItem(position); - final ChatViewHolder chatViewHolder; - if (convertView == null) { - view = activity.getLayoutInflater().inflate( - R.layout.chat_viewer_item, parent, false); - ChatMessageAdapter chatMessageAdapter = new ChatMessageAdapter( - activity); - chatViewHolder = new ChatViewHolder(view, chatMessageAdapter); - chatViewHolder.list.setAdapter(chatViewHolder.chatMessageAdapter); - chatViewHolder.send.setOnClickListener(onClickListener); - chatViewHolder.title.setOnClickListener(onClickListener); - chatViewHolder.input.setOnKeyListener(onKeyListener); - chatViewHolder.input - .setOnEditorActionListener(onEditorActionListener); - chatViewHolder.input.addTextChangedListener(new TextWatcher() { - - @Override - public void onTextChanged(CharSequence s, int start, - int before, int count) { - } - - @Override - public void beforeTextChanged(CharSequence s, int start, - int count, int after) { - } - - @Override - public void afterTextChanged(Editable s) { - if (onTextChangedListener != null) - onTextChangedListener.onTextChanged( - chatViewHolder.input, s); - } - - }); - chatViewHolder.list - .setOnCreateContextMenuListener(onCreateContextMenuListener); - view.setTag(chatViewHolder); - } else { - view = convertView; - chatViewHolder = (ChatViewHolder) view.getTag(); - } - final String account = chat.getAccount(); - final String user = chat.getUser(); - final AbstractContact abstractContact = RosterManager.getInstance() - .getBestContact(account, user); - - if (chat.equals(chatViewHolder.chatMessageAdapter.getAccount(), - chatViewHolder.chatMessageAdapter.getUser())) { - chatViewHolder.chatMessageAdapter.updateInfo(); - } else { - if (chatViewHolder.chatMessageAdapter.getAccount() != null - && chatViewHolder.chatMessageAdapter.getUser() != null) - saveState(view); - if (PageSwitcher.LOG) - LogManager.i(this, "Load " + view + " for " - + chatViewHolder.chatMessageAdapter.getUser() + " in " - + chatViewHolder.chatMessageAdapter.getAccount()); - OnTextChangedListener temp = onTextChangedListener; - onTextChangedListener = null; - chatViewHolder.input.setText(ChatManager.getInstance() - .getTypedMessage(account, user)); - chatViewHolder.input.setSelection(ChatManager.getInstance() - .getSelectionStart(account, user), ChatManager - .getInstance().getSelectionEnd(account, user)); - onTextChangedListener = temp; - chatViewHolder.chatMessageAdapter.setChat(account, user); - chatViewHolder.list.setAdapter(chatViewHolder.list.getAdapter()); - } - - chatViewHolder.page.setText(activity.getString(R.string.chat_page, - position + 1, getCount())); - ContactTitleInflater.updateTitle(chatViewHolder.title, activity, abstractContact); - avatarInflaterHelper.updateAvatar(chatViewHolder.avatar, - abstractContact); - SecurityLevel securityLevel = OTRManager.getInstance() - .getSecurityLevel(chat.getAccount(), chat.getUser()); - SecurityOtrMode securityOtrMode = SettingsManager.securityOtrMode(); - if (securityLevel == SecurityLevel.plain - && (securityOtrMode == SecurityOtrMode.disabled || securityOtrMode == SecurityOtrMode.manual)) { - chatViewHolder.security.setVisibility(View.GONE); - } else { - chatViewHolder.security.setVisibility(View.VISIBLE); - chatViewHolder.security - .setImageLevel(securityLevel.getImageLevel()); - } - return view; - } - - @Override - public void saveState(View view) { - ChatViewHolder chatViewHolder = (ChatViewHolder) view.getTag(); - if (PageSwitcher.LOG) - LogManager.i(this, "Save " + view + " for " - + chatViewHolder.chatMessageAdapter.getUser() + " in " - + chatViewHolder.chatMessageAdapter.getAccount()); - ChatManager.getInstance().setTyped( - chatViewHolder.chatMessageAdapter.getAccount(), - chatViewHolder.chatMessageAdapter.getUser(), - chatViewHolder.input.getText().toString(), - chatViewHolder.input.getSelectionStart(), - chatViewHolder.input.getSelectionEnd()); - } - - /** - * Must be called on changes in chat (message sent, received, etc.). - */ - public void onChatChange(View view, boolean incomingMessage) { - ChatViewHolder holder = (ChatViewHolder) view.getTag(); - if (incomingMessage) - holder.nameHolder.startAnimation(shake); - holder.chatMessageAdapter.onChange(); - } - - @Override - public void onChange() { - activeChats = new ArrayList(MessageManager.getInstance() - .getActiveChats()); - if (intentPosition != -1) { - int index = activeChats.indexOf(intent); - AbstractChat chat; - if (index == -1) - chat = intent; - else - chat = activeChats.remove(index); - activeChats.add(Math.min(intentPosition, activeChats.size()), chat); - } - notifyDataSetChanged(); - } - - private static class ChatViewHolder { - - final TextView page; - final View title; - final View nameHolder; - final ImageView avatar; - final ImageView security; - final View send; - final EditText input; - final ListView list; - final ChatMessageAdapter chatMessageAdapter; - - public ChatViewHolder(View view, ChatMessageAdapter chatMessageAdapter) { - page = (TextView) view.findViewById(R.id.chat_page); - title = view.findViewById(R.id.title); - nameHolder = title.findViewById(R.id.name_holder); - avatar = (ImageView) title.findViewById(R.id.avatar); - security = (ImageView) title.findViewById(R.id.security); - send = view.findViewById(R.id.chat_send); - input = (EditText) view.findViewById(R.id.chat_input); - list = (ListView) view.findViewById(android.R.id.list); - this.chatMessageAdapter = chatMessageAdapter; - } - - } - -} +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; + +import static java.lang.Math.abs; + +public class ChatViewerAdapter extends FragmentStatePagerAdapter { + + /** + * Intent sent while opening chat activity. + */ + private final AbstractChat intent; + + private ArrayList activeChats; + + private FinishUpdateListener finishUpdateListener; + + private static final int TOTAL_COUNT = 200; + private static final int OFFSET = TOTAL_COUNT / 2; + + private Fragment currentFragment; + + public ChatViewerAdapter(FragmentManager fragmentManager, BaseEntity chat, FinishUpdateListener finishUpdateListener) { + super(fragmentManager); + this.finishUpdateListener = finishUpdateListener; + + activeChats = new ArrayList<>(MessageManager.getInstance().getActiveChats()); + intent = MessageManager.getInstance().getOrCreateChat(chat.getAccount(), Jid.getBareAddress(chat.getUser())); + + updateChats(); + } + + public ChatViewerAdapter(FragmentManager fragmentManager, FinishUpdateListener finishUpdateListener) { + super(fragmentManager); + this.finishUpdateListener = finishUpdateListener; + + activeChats = new ArrayList<>(MessageManager.getInstance().getActiveChats()); + intent = null; + updateChats(); + } + + @Override + public int getCount() { + // warning: scrolling to very high values (1,000,000+) results in + // strange drawing behaviour + return TOTAL_COUNT; + } + + public int getRealCount() { + return activeChats.size() + 1; + } + + @Override + public Fragment getItem(int virtualPagePosition) { + int realPosition = getRealPagePosition(virtualPagePosition); + + if (realPosition == 0) { + return RecentChatFragment.newInstance(); + } + + AbstractChat chat = activeChats.get(getChatIndexFromRealPosition(realPosition)); + return ChatViewerFragment.newInstance(chat.getAccount(), chat.getUser()); + } + + public boolean updateChats() { + + ArrayList newChats = new ArrayList<>(MessageManager.getInstance().getActiveChats()); + + if (intent != null && !newChats.contains(intent)) { + newChats.add(intent); + } + + Collections.sort(newChats, new Comparator() { + @Override + public int compare(AbstractChat lhs, AbstractChat rhs) { + return lhs.getCreationTime().compareTo(rhs.getCreationTime()); + } + }); + + + if (isChatsEquals(newChats)) { + return false; + } + + activeChats = newChats; + notifyDataSetChanged(); + + return true; + } + + private boolean isChatsEquals(ArrayList newChats) { + if (newChats.size() != activeChats.size()) { + return false; + } + + for (int i = 0; i < activeChats.size(); i++) { + AbstractChat oldChat = activeChats.get(i); + AbstractChat newChat = newChats.get(i); + + if (!oldChat.equals(newChat.getAccount(), newChat.getUser())) { + return false; + } + } + return true; + } + + public int getPageNumber(BaseEntity chat) { + int realPosition = 0; + + for (int chatIndex = 0; chatIndex < activeChats.size(); chatIndex++) { + if (activeChats.get(chatIndex).equals(chat)) { + realPosition = chatIndex + 1; + break; + } + } + + return realPosition + OFFSET; + } + + public AbstractChat getChatByPageNumber(int virtualPosition) { + int realPosition = getRealPagePosition(virtualPosition); + + if (realPosition == 0) { + return null; + } + return activeChats.get(getChatIndexFromRealPosition(realPosition)); + } + + + public int getRealPagePosition(int virtualPosition) { + int realCount = getRealCount(); + + int pageNumber = abs(virtualPosition - OFFSET) % realCount; + if (virtualPosition >= OFFSET) { + return pageNumber; + } else { + return pageNumber == 0 ? 0 : realCount - pageNumber; + } + + } + + private int getChatIndexFromRealPosition(int realPosition) { + return realPosition - 1; + } + + @Override + public void finishUpdate(ViewGroup container) { + super.finishUpdate(container); + + finishUpdateListener.onChatViewAdapterFinishUpdate(); + } + + public interface FinishUpdateListener { + void onChatViewAdapterFinishUpdate(); + } + + public ArrayList getActiveChats() { + return activeChats; + } + + @Override + public int getItemPosition(Object object) { + return POSITION_NONE; + } + + @Override + public void setPrimaryItem(ViewGroup container, int position, Object object) { + super.setPrimaryItem(container, position, object); + + if (currentFragment instanceof ChatViewerFragment) { + ((ChatViewerFragment)currentFragment).saveInputState(); + } + + currentFragment = (Fragment) object; + } + + public Fragment getCurrentFragment() { + return currentFragment; + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/ui/adapter/ClientContactInflater.java b/app/src/main/java/com/xabber/android/ui/adapter/ClientContactInflater.java deleted file mode 100644 index 8e36e17de2..0000000000 --- a/app/src/main/java/com/xabber/android/ui/adapter/ClientContactInflater.java +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.xabber.android.ui.adapter; - -import android.app.Activity; -import android.view.View; -import android.widget.ImageView; - -import com.xabber.android.data.extension.capability.ClientSoftware; -import com.xabber.android.data.roster.AbstractContact; -import com.xabber.androiddev.R; - -/** - * Inflate view with contact's client software. - * - * @author alexander.ivanov - * - */ -public class ClientContactInflater extends StatusContactInflater { - - public ClientContactInflater(Activity activity) { - super(activity); - } - - @Override - ViewHolder createViewHolder(int position, View view) { - return new ClientContactInflater.ViewHolder(view); - } - - @Override - public void getView(View view, AbstractContact abstractContact) { - super.getView(view, abstractContact); - ViewHolder viewHolder = (ViewHolder) view.getTag(); - ClientSoftware clientSoftware = abstractContact.getClientSoftware(); - if (clientSoftware == ClientSoftware.unknown) - viewHolder.clientSoftware.setVisibility(View.INVISIBLE); - else { - viewHolder.clientSoftware.setVisibility(View.VISIBLE); - viewHolder.clientSoftware.setImageLevel(clientSoftware.ordinal()); - } - } - - static class ViewHolder extends StatusContactInflater.ViewHolder { - - final ImageView clientSoftware; - - public ViewHolder(View view) { - super(view); - clientSoftware = (ImageView) view - .findViewById(R.id.client_software); - } - - } - -} diff --git a/app/src/main/java/com/xabber/android/ui/adapter/ComparatorByChat.java b/app/src/main/java/com/xabber/android/ui/adapter/ComparatorByChat.java index 74ebb70859..04a7438a32 100644 --- a/app/src/main/java/com/xabber/android/ui/adapter/ComparatorByChat.java +++ b/app/src/main/java/com/xabber/android/ui/adapter/ComparatorByChat.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,31 +20,31 @@ public class ComparatorByChat extends ComparatorByName { - public static final ComparatorByChat COMPARATOR_BY_CHAT = new ComparatorByChat(); + public static final ComparatorByChat COMPARATOR_BY_CHAT = new ComparatorByChat(); - @Override - public int compare(AbstractContact object1, AbstractContact object2) { - final MessageManager messageManager = MessageManager.getInstance(); - final AbstractChat abstractChat1 = messageManager.getChat( - object1.getAccount(), object1.getUser()); - final AbstractChat abstractChat2 = messageManager.getChat( - object2.getAccount(), object2.getUser()); - final boolean hasActiveChat1 = abstractChat1 != null - && abstractChat1.isActive(); - final boolean hasActiveChat2 = abstractChat2 != null - && abstractChat2.isActive(); - if (hasActiveChat1 && !hasActiveChat2) - return -1; - if (!hasActiveChat1 && hasActiveChat2) - return 1; - if (hasActiveChat1) { - int result; - result = ChatComparator.CHAT_COMPARATOR.compare(abstractChat1, - abstractChat2); - if (result != 0) - return result; - } - return super.compare(object1, object2); - } + @Override + public int compare(AbstractContact object1, AbstractContact object2) { + final MessageManager messageManager = MessageManager.getInstance(); + final AbstractChat abstractChat1 = messageManager.getChat( + object1.getAccount(), object1.getUser()); + final AbstractChat abstractChat2 = messageManager.getChat( + object2.getAccount(), object2.getUser()); + final boolean hasActiveChat1 = abstractChat1 != null + && abstractChat1.isActive(); + final boolean hasActiveChat2 = abstractChat2 != null + && abstractChat2.isActive(); + if (hasActiveChat1 && !hasActiveChat2) + return -1; + if (!hasActiveChat1 && hasActiveChat2) + return 1; + if (hasActiveChat1) { + int result; + result = ChatComparator.CHAT_COMPARATOR.compare(abstractChat1, + abstractChat2); + if (result != 0) + return result; + } + return super.compare(object1, object2); + } } diff --git a/app/src/main/java/com/xabber/android/ui/adapter/ComparatorByName.java b/app/src/main/java/com/xabber/android/ui/adapter/ComparatorByName.java index 3c77140b6e..b8b293f0f2 100644 --- a/app/src/main/java/com/xabber/android/ui/adapter/ComparatorByName.java +++ b/app/src/main/java/com/xabber/android/ui/adapter/ComparatorByName.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,15 +20,15 @@ public class ComparatorByName implements Comparator { - public static final ComparatorByName COMPARATOR_BY_NAME = new ComparatorByName(); + public static final ComparatorByName COMPARATOR_BY_NAME = new ComparatorByName(); - @Override - public int compare(AbstractContact object1, AbstractContact object2) { - int result; - result = object1.getName().compareToIgnoreCase(object2.getName()); - if (result != 0) - return result; - return object1.getAccount().compareToIgnoreCase(object2.getAccount()); - } + @Override + public int compare(AbstractContact object1, AbstractContact object2) { + int result; + result = object1.getName().compareToIgnoreCase(object2.getName()); + if (result != 0) + return result; + return object1.getAccount().compareToIgnoreCase(object2.getAccount()); + } } diff --git a/app/src/main/java/com/xabber/android/ui/adapter/ComparatorByStatus.java b/app/src/main/java/com/xabber/android/ui/adapter/ComparatorByStatus.java index d76fe87a36..4d60c3958f 100644 --- a/app/src/main/java/com/xabber/android/ui/adapter/ComparatorByStatus.java +++ b/app/src/main/java/com/xabber/android/ui/adapter/ComparatorByStatus.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,15 +18,15 @@ public class ComparatorByStatus extends ComparatorByName { - public static final ComparatorByStatus COMPARATOR_BY_STATUS = new ComparatorByStatus(); + public static final ComparatorByStatus COMPARATOR_BY_STATUS = new ComparatorByStatus(); - @Override - public int compare(AbstractContact object1, AbstractContact object2) { - int result; - result = object1.getStatusMode().compareTo(object2.getStatusMode()); - if (result != 0) - return result; - return super.compare(object1, object2); - } + @Override + public int compare(AbstractContact object1, AbstractContact object2) { + int result; + result = object1.getStatusMode().compareTo(object2.getStatusMode()); + if (result != 0) + return result; + return super.compare(object1, object2); + } } diff --git a/app/src/main/java/com/xabber/android/ui/adapter/ContactItemInflater.java b/app/src/main/java/com/xabber/android/ui/adapter/ContactItemInflater.java new file mode 100644 index 0000000000..a5bd63244c --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/adapter/ContactItemInflater.java @@ -0,0 +1,139 @@ +package com.xabber.android.ui.adapter; + +import android.content.Context; +import android.graphics.drawable.ColorDrawable; +import android.os.Build; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.xabber.android.R; +import com.xabber.android.data.SettingsManager; +import com.xabber.android.data.extension.capability.ClientSoftware; +import com.xabber.android.data.extension.muc.MUCManager; +import com.xabber.android.data.message.AbstractChat; +import com.xabber.android.data.message.MessageManager; +import com.xabber.android.data.roster.AbstractContact; +import com.xabber.android.ui.ContactEditor; +import com.xabber.android.ui.ContactViewer; +import com.xabber.android.utils.StringUtils; + +public class ContactItemInflater { + + final Context context; + private final int elevation; + private int[] accountMainColors; + + public ContactItemInflater(Context context) { + this.context = context; + accountMainColors = context.getResources().getIntArray(R.array.account_action_bar); + elevation = context.getResources().getDimensionPixelSize(R.dimen.contact_elevation); + } + + public View setUpContactView(View convertView, ViewGroup parent, final AbstractContact contact) { + final View view; + final ContactListItemViewHolder viewHolder; + if (convertView == null) { + view = LayoutInflater.from(context).inflate(R.layout.contact_list_item, parent, false); + viewHolder = new ContactListItemViewHolder(view); + viewHolder.statusIconSeparator.setVisibility(View.INVISIBLE); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + view.setElevation(elevation); + } + view.setTag(viewHolder); + } else { + view = convertView; + viewHolder = (ContactListItemViewHolder) view.getTag(); + } + + if (contact.isConnected()) { + viewHolder.offlineShadow.setVisibility(View.GONE); + } else { + viewHolder.offlineShadow.setVisibility(View.VISIBLE); + } + + int colorLevel = contact.getColorLevel(); + viewHolder.color.setImageDrawable(new ColorDrawable(accountMainColors[colorLevel])); + viewHolder.color.setVisibility(View.VISIBLE); + + if (SettingsManager.contactsShowAvatars()) { + viewHolder.avatar.setVisibility(View.VISIBLE); + viewHolder.avatar.setImageDrawable(contact.getAvatarForContactList()); + } else { + viewHolder.avatar.setVisibility(View.GONE); + } + + viewHolder.avatar.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onAvatarClick(contact); + } + }); + + viewHolder.name.setText(contact.getName()); + String statusText; + + viewHolder.outgoingMessageIndicator.setVisibility(View.GONE); + + ClientSoftware clientSoftware = contact.getClientSoftware(); + + MessageManager messageManager = MessageManager.getInstance(); + + viewHolder.smallRightText.setVisibility(View.GONE); + viewHolder.smallRightIcon.setVisibility(View.GONE); + + if (messageManager.hasActiveChat(contact.getAccount(), contact.getUser())) { + + AbstractChat chat = messageManager.getChat(contact.getAccount(), contact.getUser()); + + statusText = chat.getLastText().trim(); + + view.setBackgroundColor(context.getResources().getColor(R.color.contact_list_active_chat_background)); + + if (!statusText.isEmpty()) { + + viewHolder.smallRightText.setText(StringUtils.getSmartTimeText(context, chat.getLastTime())); + viewHolder.smallRightText.setVisibility(View.VISIBLE); + + if (!chat.isLastMessageIncoming()) { + viewHolder.outgoingMessageIndicator.setText(context.getString(R.string.sender_is_you) + ": "); + viewHolder.outgoingMessageIndicator.setVisibility(View.VISIBLE); + viewHolder.outgoingMessageIndicator.setTextColor(accountMainColors[colorLevel]); + } + viewHolder.smallRightIcon.setImageResource(R.drawable.ic_client_small); + viewHolder.smallRightIcon.setVisibility(View.VISIBLE); + viewHolder.smallRightIcon.setImageLevel(clientSoftware.ordinal()); + viewHolder.largeClientIcon.setVisibility(View.GONE); + } else { + viewHolder.largeClientIcon.setVisibility(View.VISIBLE); + viewHolder.largeClientIcon.setImageLevel(clientSoftware.ordinal()); + } + } else { + statusText = contact.getStatusText().trim(); + view.setBackgroundColor(context.getResources().getColor(R.color.contact_list_contact_background)); + viewHolder.largeClientIcon.setVisibility(View.VISIBLE); + viewHolder.largeClientIcon.setImageLevel(clientSoftware.ordinal()); + } + + if (statusText.isEmpty()) { + viewHolder.secondLineMessage.setVisibility(View.GONE); + } else { + viewHolder.secondLineMessage.setVisibility(View.VISIBLE); + viewHolder.secondLineMessage.setText(statusText.trim()); + } + + viewHolder.statusIcon.setImageLevel(contact.getStatusMode().getStatusLevel()); + return view; + } + + private void onAvatarClick(AbstractContact contact) { + if (MUCManager.getInstance().hasRoom(contact.getAccount(), contact.getUser())) { + context.startActivity(ContactViewer.createIntent(context, + contact.getAccount(), contact.getUser())); + } else { + context.startActivity(ContactEditor.createIntent(context, + contact.getAccount(), contact.getUser())); + } + } +} diff --git a/app/src/main/java/com/xabber/android/ui/adapter/ContactListAdapter.java b/app/src/main/java/com/xabber/android/ui/adapter/ContactListAdapter.java index ad59a3b874..6199e9cc57 100644 --- a/app/src/main/java/com/xabber/android/ui/adapter/ContactListAdapter.java +++ b/app/src/main/java/com/xabber/android/ui/adapter/ContactListAdapter.java @@ -1,38 +1,28 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.ui.adapter; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.Date; -import java.util.Map.Entry; -import java.util.TreeMap; - -import android.app.ListActivity; +import android.app.Activity; import android.os.Handler; -import android.view.View; -import android.view.animation.Animation; -import android.view.animation.AnimationUtils; -import android.widget.Button; -import android.widget.TextView; +import android.widget.Filter; +import android.widget.Filterable; import com.xabber.android.data.SettingsManager; import com.xabber.android.data.account.AccountManager; import com.xabber.android.data.account.CommonState; +import com.xabber.android.data.entity.BaseEntity; import com.xabber.android.data.extension.muc.RoomChat; import com.xabber.android.data.extension.muc.RoomContact; import com.xabber.android.data.message.AbstractChat; @@ -42,477 +32,445 @@ import com.xabber.android.data.roster.GroupManager; import com.xabber.android.data.roster.RosterContact; import com.xabber.android.data.roster.RosterManager; -import com.xabber.androiddev.R; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.TreeMap; /** * Adapter for contact list in the main activity. - * + * * @author alexander.ivanov - * */ -public class ContactListAdapter extends - GroupedContactAdapter implements - Runnable { - - /** - * Number of milliseconds between lazy refreshes. - */ - private static final long REFRESH_INTERVAL = 1000; - - /** - * View with information shown on empty contact list. - */ - private final View infoView; - - /** - * Image view with connected icon. - */ - private View connectedView; - - /** - * Image view with disconnected icon. - */ - private View disconnectedView; - - /** - * View with help text. - */ - private TextView textView; - - /** - * Button to apply help text. - */ - private Button buttonView; - - /** - * Animation for disconnected view. - */ - private Animation animation; - - /** - * Handler for deferred refresh. - */ - private final Handler handler; - - /** - * Lock for refresh requests. - */ - private final Object refreshLock; - - /** - * Whether refresh was requested. - */ - private boolean refreshRequested; - - /** - * Whether refresh is in progress. - */ - private boolean refreshInProgess; - - /** - * Minimal time when next refresh can be executed. - */ - private Date nextRefresh; - - public ContactListAdapter(ListActivity activity) { - super(activity, activity.getListView(), new ChatContactInflater( - activity), GroupManager.getInstance()); - infoView = activity.findViewById(R.id.info); - if (infoView != null) { - connectedView = infoView.findViewById(R.id.connected); - disconnectedView = infoView.findViewById(R.id.disconnected); - textView = (TextView) infoView.findViewById(R.id.text); - buttonView = (Button) infoView.findViewById(R.id.button); - animation = AnimationUtils.loadAnimation(activity, - R.anim.connection); - } else { - connectedView = null; - disconnectedView = null; - textView = null; - buttonView = null; - animation = null; - } - handler = new Handler(); - refreshLock = new Object(); - refreshRequested = false; - refreshInProgess = false; - nextRefresh = new Date(); - } - - /** - * Requests refresh in some time in future. - */ - public void refreshRequest() { - synchronized (refreshLock) { - if (refreshRequested) - return; - if (refreshInProgess) - refreshRequested = true; - else { - long delay = nextRefresh.getTime() - new Date().getTime(); - handler.postDelayed(this, delay > 0 ? delay : 0); - } - } - } - - /** - * Remove refresh requests. - */ - public void removeRefreshRequests() { - synchronized (refreshLock) { - refreshRequested = false; - refreshInProgess = false; - handler.removeCallbacks(this); - } - } - - @Override - public void onChange() { - synchronized (refreshLock) { - refreshRequested = false; - refreshInProgess = true; - handler.removeCallbacks(this); - } - - final Collection rosterContacts = RosterManager - .getInstance().getContacts(); - final boolean showOffline = SettingsManager.contactsShowOffline(); - final boolean showGroups = SettingsManager.contactsShowGroups(); - final boolean showEmptyGroups = SettingsManager - .contactsShowEmptyGroups(); - final boolean showActiveChats = SettingsManager - .contactsShowActiveChats(); - final boolean stayActiveChats = SettingsManager - .contactsStayActiveChats(); - final boolean showAccounts = SettingsManager.contactsShowAccounts(); - final Comparator comparator = SettingsManager - .contactsOrder(); - final CommonState commonState = AccountManager.getInstance() - .getCommonState(); - final String selectedAccount = AccountManager.getInstance() - .getSelectedAccount(); - - /** - * Accounts. - */ - final TreeMap accounts = new TreeMap(); - - /** - * Groups. - */ - final TreeMap groups; - - /** - * Contacts. - */ - final ArrayList contacts; - - /** - * List of active chats. - */ - final GroupConfiguration activeChats; - - /** - * List of rooms and active chats grouped by users inside accounts. - */ - final TreeMap> abstractChats = new TreeMap>(); - - /** - * Whether there is at least one contact. - */ - boolean hasContact = false; - - /** - * Whether there is at least one visible contact. - */ - boolean hasVisible = false; - - for (String account : AccountManager.getInstance().getAccounts()) - accounts.put(account, null); - - for (AbstractChat abstractChat : MessageManager.getInstance() - .getChats()) { - if ((abstractChat instanceof RoomChat || abstractChat.isActive()) - && accounts.containsKey(abstractChat.getAccount())) { - final String account = abstractChat.getAccount(); - TreeMap users = abstractChats - .get(account); - if (users == null) { - users = new TreeMap(); - abstractChats.put(account, users); - } - users.put(abstractChat.getUser(), abstractChat); - } - } - - if (filterString == null) { - // Create arrays. - if (showAccounts) { - groups = null; - contacts = null; - for (Entry entry : accounts - .entrySet()) { - entry.setValue(new AccountConfiguration(entry.getKey(), - GroupManager.IS_ACCOUNT, groupStateProvider)); - } - } else { - if (showGroups) { - groups = new TreeMap(); - contacts = null; - } else { - groups = null; - contacts = new ArrayList(); - } - } - if (showActiveChats) - activeChats = new GroupConfiguration(GroupManager.NO_ACCOUNT, - GroupManager.ACTIVE_CHATS, groupStateProvider); - else - activeChats = null; - - // Build structure. - for (RosterContact rosterContact : rosterContacts) { - if (!rosterContact.isEnabled()) - continue; - hasContact = true; - final boolean online = rosterContact.getStatusMode().isOnline(); - final String account = rosterContact.getAccount(); - final TreeMap users = abstractChats - .get(account); - final AbstractChat abstractChat; - if (users == null) - abstractChat = null; - else - abstractChat = users.remove(rosterContact.getUser()); - if (showActiveChats && abstractChat != null - && abstractChat.isActive()) { - activeChats.setNotEmpty(); - hasVisible = true; - if (activeChats.isExpanded()) - activeChats.addAbstractContact(rosterContact); - activeChats.increment(online); - if (!stayActiveChats || (!showAccounts && !showGroups)) - continue; - } - if (selectedAccount != null && !selectedAccount.equals(account)) - continue; - if (addContact(rosterContact, online, accounts, groups, - contacts, showAccounts, showGroups, showOffline)) - hasVisible = true; - } - for (TreeMap users : abstractChats.values()) - for (AbstractChat abstractChat : users.values()) { - final AbstractContact abstractContact; - if (abstractChat instanceof RoomChat) - abstractContact = new RoomContact( - (RoomChat) abstractChat); - else - abstractContact = new ChatContact(abstractChat); - if (showActiveChats && abstractChat.isActive()) { - activeChats.setNotEmpty(); - hasVisible = true; - if (activeChats.isExpanded()) - activeChats.addAbstractContact(abstractContact); - activeChats.increment(false); - if (!stayActiveChats || (!showAccounts && !showGroups)) - continue; - } - if (selectedAccount != null - && !selectedAccount.equals(abstractChat - .getAccount())) - continue; - final String group; - final boolean online; - if (abstractChat instanceof RoomChat) { - group = GroupManager.IS_ROOM; - online = abstractContact.getStatusMode().isOnline(); - } else { - group = GroupManager.NO_GROUP; - online = false; - } - hasVisible = true; - addContact(abstractContact, group, online, accounts, - groups, contacts, showAccounts, showGroups); - } - - // Remove empty groups, sort and apply structure. - baseEntities.clear(); - if (hasVisible) { - if (showActiveChats) { - if (!activeChats.isEmpty()) { - if (showAccounts || showGroups) - baseEntities.add(activeChats); - activeChats - .sortAbstractContacts(ComparatorByChat.COMPARATOR_BY_CHAT); - baseEntities.addAll(activeChats.getAbstractContacts()); - } - } - if (showAccounts) { - for (AccountConfiguration rosterAccount : accounts.values()) { - baseEntities.add(rosterAccount); - if (showGroups) { - if (rosterAccount.isExpanded()) - for (GroupConfiguration rosterConfiguration : rosterAccount - .getSortedGroupConfigurations()) - if (showEmptyGroups - || !rosterConfiguration.isEmpty()) { - baseEntities.add(rosterConfiguration); - rosterConfiguration - .sortAbstractContacts(comparator); - baseEntities.addAll(rosterConfiguration - .getAbstractContacts()); - } - } else { - rosterAccount.sortAbstractContacts(comparator); - baseEntities.addAll(rosterAccount - .getAbstractContacts()); - } - } - } else { - if (showGroups) { - for (GroupConfiguration rosterConfiguration : groups - .values()) - if (showEmptyGroups - || !rosterConfiguration.isEmpty()) { - baseEntities.add(rosterConfiguration); - rosterConfiguration - .sortAbstractContacts(comparator); - baseEntities.addAll(rosterConfiguration - .getAbstractContacts()); - } - } else { - Collections.sort(contacts, comparator); - baseEntities.addAll(contacts); - } - } - } - } else { // Search - final ArrayList baseEntities = new ArrayList(); - - // Build structure. - for (RosterContact rosterContact : rosterContacts) { - if (!rosterContact.isEnabled()) - continue; - final String account = rosterContact.getAccount(); - final TreeMap users = abstractChats - .get(account); - if (users != null) - users.remove(rosterContact.getUser()); - if (rosterContact.getName().toLowerCase(locale) - .contains(filterString)) - baseEntities.add(rosterContact); - } - for (TreeMap users : abstractChats.values()) - for (AbstractChat abstractChat : users.values()) { - final AbstractContact abstractContact; - if (abstractChat instanceof RoomChat) - abstractContact = new RoomContact( - (RoomChat) abstractChat); - else - abstractContact = new ChatContact(abstractChat); - if (abstractContact.getName().toLowerCase(locale) - .contains(filterString)) - baseEntities.add(abstractContact); - } - Collections.sort(baseEntities, comparator); - this.baseEntities.clear(); - this.baseEntities.addAll(baseEntities); - hasVisible = baseEntities.size() > 0; - } - - if (infoView != null) { - if (hasVisible) { - infoView.setVisibility(View.GONE); - disconnectedView.clearAnimation(); - } else { - infoView.setVisibility(View.VISIBLE); - final int text; - final int button; - final ContactListState state; - if (filterString != null) { - if (commonState == CommonState.online) - state = ContactListState.online; - else if (commonState == CommonState.roster - || commonState == CommonState.connecting) - state = ContactListState.connecting; - else - state = ContactListState.offline; - text = R.string.application_state_no_online; - button = 0; - } else if (hasContact) { - state = ContactListState.online; - text = R.string.application_state_no_online; - button = R.string.application_action_no_online; - } else if (commonState == CommonState.online) { - state = ContactListState.online; - text = R.string.application_state_no_contacts; - button = R.string.application_action_no_contacts; - } else if (commonState == CommonState.roster) { - state = ContactListState.connecting; - text = R.string.application_state_roster; - button = 0; - } else if (commonState == CommonState.connecting) { - state = ContactListState.connecting; - text = R.string.application_state_connecting; - button = 0; - } else if (commonState == CommonState.waiting) { - state = ContactListState.offline; - text = R.string.application_state_waiting; - button = R.string.application_action_waiting; - } else if (commonState == CommonState.offline) { - state = ContactListState.offline; - text = R.string.application_state_offline; - button = R.string.application_action_offline; - } else if (commonState == CommonState.disabled) { - state = ContactListState.offline; - text = R.string.application_state_disabled; - button = R.string.application_action_disabled; - } else if (commonState == CommonState.empty) { - state = ContactListState.offline; - text = R.string.application_state_empty; - button = R.string.application_action_empty; - } else { - throw new IllegalStateException(); - } - if (state == ContactListState.offline) { - connectedView.setVisibility(View.INVISIBLE); - disconnectedView.setVisibility(View.VISIBLE); - disconnectedView.clearAnimation(); - } else if (state == ContactListState.connecting) { - connectedView.setVisibility(View.VISIBLE); - disconnectedView.setVisibility(View.VISIBLE); - if (disconnectedView.getAnimation() == null) - disconnectedView.startAnimation(animation); - } else if (state == ContactListState.online) { - connectedView.setVisibility(View.VISIBLE); - disconnectedView.setVisibility(View.INVISIBLE); - disconnectedView.clearAnimation(); - } - textView.setText(text); - if (button == 0) { - buttonView.setVisibility(View.GONE); - } else { - buttonView.setVisibility(View.VISIBLE); - buttonView.setText(button); - buttonView.setTag(Integer.valueOf(button)); - } - } - } - - super.onChange(); - - synchronized (refreshLock) { - nextRefresh = new Date(new Date().getTime() + REFRESH_INTERVAL); - refreshInProgess = false; - handler.removeCallbacks(this); // Just to be sure. - if (refreshRequested) - handler.postDelayed(this, REFRESH_INTERVAL); - } - } - - @Override - public void run() { - onChange(); - } - +public class ContactListAdapter extends GroupedContactAdapter implements Runnable, Filterable { + + /** + * Number of milliseconds between lazy refreshes. + */ + private static final long REFRESH_INTERVAL = 1000; + + /** + * Handler for deferred refresh. + */ + private final Handler handler; + + /** + * Lock for refresh requests. + */ + private final Object refreshLock; + + /** + * Whether refresh was requested. + */ + private boolean refreshRequested; + + /** + * Whether refresh is in progress. + */ + private boolean refreshInProgress; + + /** + * Minimal time when next refresh can be executed. + */ + private Date nextRefresh; + + /** + * Contact filter. + */ + ContactFilter contactFilter; + + /** + * Filter string. Can be null if filter is disabled. + */ + String filterString; + + private final OnContactListChangedListener listener; + private boolean hasActiveChats = false; + + public ContactListAdapter(Activity activity, OnContactListChangedListener listener, + OnClickListener onClickListener) { + super(activity, onClickListener); + this.listener = listener; + handler = new Handler(); + refreshLock = new Object(); + refreshRequested = false; + refreshInProgress = false; + nextRefresh = new Date(); + } + + /** + * Requests refresh in some time in future. + */ + public void refreshRequest() { + synchronized (refreshLock) { + if (refreshRequested) { + return; + } + if (refreshInProgress) { + refreshRequested = true; + } else { + long delay = nextRefresh.getTime() - new Date().getTime(); + handler.postDelayed(this, delay > 0 ? delay : 0); + } + } + } + + /** + * Remove refresh requests. + */ + public void removeRefreshRequests() { + synchronized (refreshLock) { + refreshRequested = false; + refreshInProgress = false; + handler.removeCallbacks(this); + } + } + + @Override + public void onChange() { + synchronized (refreshLock) { + refreshRequested = false; + refreshInProgress = true; + handler.removeCallbacks(this); + } + + final Collection rosterContacts = RosterManager.getInstance().getContacts(); + final boolean showOffline = SettingsManager.contactsShowOffline(); + final boolean showGroups = SettingsManager.contactsShowGroups(); + final boolean showEmptyGroups = SettingsManager.contactsShowEmptyGroups(); + final boolean showActiveChats = SettingsManager.contactsShowActiveChats(); + final boolean stayActiveChats = SettingsManager.contactsStayActiveChats(); + final boolean showAccounts = SettingsManager.contactsShowAccounts(); + final Comparator comparator = SettingsManager.contactsOrder(); + final CommonState commonState = AccountManager.getInstance().getCommonState(); + final String selectedAccount = AccountManager.getInstance().getSelectedAccount(); + + + /** + * Groups. + */ + final Map groups; + + /** + * Contacts. + */ + final List contacts; + + /** + * List of active chats. + */ + final GroupConfiguration activeChats; + + /** + * Whether there is at least one contact. + */ + boolean hasContacts = false; + + /** + * Whether there is at least one visible contact. + */ + boolean hasVisibleContacts = false; + + final Map accounts = new TreeMap<>(); + + for (String account : AccountManager.getInstance().getAccounts()) { + accounts.put(account, null); + } + + /** + * List of rooms and active chats grouped by users inside accounts. + */ + final Map> abstractChats = new TreeMap<>(); + + for (AbstractChat abstractChat : MessageManager.getInstance().getChats()) { + if ((abstractChat instanceof RoomChat || abstractChat.isActive()) + && accounts.containsKey(abstractChat.getAccount())) { + final String account = abstractChat.getAccount(); + Map users = abstractChats.get(account); + if (users == null) { + users = new TreeMap<>(); + abstractChats.put(account, users); + } + users.put(abstractChat.getUser(), abstractChat); + } + } + + if (filterString == null) { + // Create arrays. + if (showAccounts) { + groups = null; + contacts = null; + for (Entry entry : accounts.entrySet()) { + entry.setValue(new AccountConfiguration(entry.getKey(), + GroupManager.IS_ACCOUNT, GroupManager.getInstance())); + } + } else { + if (showGroups) { + groups = new TreeMap<>(); + contacts = null; + } else { + groups = null; + contacts = new ArrayList<>(); + } + } + if (showActiveChats) { + activeChats = new GroupConfiguration(GroupManager.NO_ACCOUNT, + GroupManager.ACTIVE_CHATS, GroupManager.getInstance()); + } else { + activeChats = null; + } + + // Build structure. + for (RosterContact rosterContact : rosterContacts) { + if (!rosterContact.isEnabled()) { + continue; + } + hasContacts = true; + final boolean online = rosterContact.getStatusMode().isOnline(); + final String account = rosterContact.getAccount(); + final Map users = abstractChats.get(account); + final AbstractChat abstractChat; + if (users == null) { + abstractChat = null; + } else { + abstractChat = users.remove(rosterContact.getUser()); + } + if (showActiveChats && abstractChat != null && abstractChat.isActive()) { + activeChats.setNotEmpty(); + hasVisibleContacts = true; + if (activeChats.isExpanded()) { + activeChats.addAbstractContact(rosterContact); + } + activeChats.increment(online); + if (!stayActiveChats || (!showAccounts && !showGroups)) { + continue; + } + } + if (selectedAccount != null && !selectedAccount.equals(account)) { + continue; + } + if (addContact(rosterContact, online, accounts, groups, + contacts, showAccounts, showGroups, showOffline)) { + hasVisibleContacts = true; + } + } + for (Map users : abstractChats.values()) { + for (AbstractChat abstractChat : users.values()) { + final AbstractContact abstractContact; + if (abstractChat instanceof RoomChat) { + abstractContact = new RoomContact((RoomChat) abstractChat); + } else { + abstractContact = new ChatContact(abstractChat); + } + if (showActiveChats && abstractChat.isActive()) { + activeChats.setNotEmpty(); + hasVisibleContacts = true; + if (activeChats.isExpanded()) { + activeChats.addAbstractContact(abstractContact); + } + activeChats.increment(false); + if (!stayActiveChats || (!showAccounts && !showGroups)) { + continue; + } + } + if (selectedAccount != null && !selectedAccount.equals(abstractChat.getAccount())) { + continue; + } + final String group; + final boolean online; + if (abstractChat instanceof RoomChat) { + group = GroupManager.IS_ROOM; + online = abstractContact.getStatusMode().isOnline(); + } else { + group = GroupManager.NO_GROUP; + online = false; + } + hasVisibleContacts = true; + addContact(abstractContact, group, online, accounts, groups, contacts, + showAccounts, showGroups); + } + } + + hasActiveChats = activeChats != null && activeChats.getTotal() > 0; + + // Remove empty groups, sort and apply structure. + baseEntities.clear(); + if (hasVisibleContacts) { + if (showActiveChats) { + if (!activeChats.isEmpty()) { + if (showAccounts || showGroups) { + baseEntities.add(activeChats); + } + activeChats.sortAbstractContacts(ComparatorByChat.COMPARATOR_BY_CHAT); + baseEntities.addAll(activeChats.getAbstractContacts()); + } + } + if (showAccounts) { + boolean isFirst = baseEntities.isEmpty(); + for (AccountConfiguration rosterAccount : accounts.values()) { + if (isFirst) { + isFirst = false; + } else { + baseEntities.add(new AccountTopSeparator(null, null)); + } + + baseEntities.add(rosterAccount); + + if (showGroups) { + if (rosterAccount.isExpanded()) { + for (GroupConfiguration rosterConfiguration : rosterAccount + .getSortedGroupConfigurations()) { + if (showEmptyGroups || !rosterConfiguration.isEmpty()) { + baseEntities.add(rosterConfiguration); + rosterConfiguration.sortAbstractContacts(comparator); + baseEntities.addAll(rosterConfiguration.getAbstractContacts()); + } + } + } + } else { + rosterAccount.sortAbstractContacts(comparator); + baseEntities.addAll(rosterAccount.getAbstractContacts()); + } + + if (rosterAccount.getTotal() > 0 && !rosterAccount.isExpanded()) { + baseEntities.add(new AccountBottomSeparator(rosterAccount.getAccount(), null)); + } + } + } else { + if (showGroups) { + for (GroupConfiguration rosterConfiguration : groups.values()) { + if (showEmptyGroups || !rosterConfiguration.isEmpty()) { + baseEntities.add(rosterConfiguration); + rosterConfiguration.sortAbstractContacts(comparator); + baseEntities.addAll(rosterConfiguration.getAbstractContacts()); + } + } + } else { + Collections.sort(contacts, comparator); + baseEntities.addAll(contacts); + } + } + } + } else { // Search + final ArrayList baseEntities = getSearchResults(rosterContacts, comparator, abstractChats); + this.baseEntities.clear(); + this.baseEntities.addAll(baseEntities); + hasVisibleContacts = baseEntities.size() > 0; + } + + super.onChange(); + listener.onContactListChanged(commonState, hasContacts, hasVisibleContacts, filterString != null); + + synchronized (refreshLock) { + nextRefresh = new Date(new Date().getTime() + REFRESH_INTERVAL); + refreshInProgress = false; + handler.removeCallbacks(this); // Just to be sure. + if (refreshRequested) { + handler.postDelayed(this, REFRESH_INTERVAL); + } + } + } + + private ArrayList getSearchResults(Collection rosterContacts, + Comparator comparator, + Map> abstractChats) { + final ArrayList baseEntities = new ArrayList<>(); + + // Build structure. + for (RosterContact rosterContact : rosterContacts) { + if (!rosterContact.isEnabled()) { + continue; + } + final String account = rosterContact.getAccount(); + final Map users = abstractChats.get(account); + if (users != null) { + users.remove(rosterContact.getUser()); + } + if (rosterContact.getName().toLowerCase(locale).contains(filterString)) { + baseEntities.add(rosterContact); + } + } + for (Map users : abstractChats.values()) { + for (AbstractChat abstractChat : users.values()) { + final AbstractContact abstractContact; + if (abstractChat instanceof RoomChat) { + abstractContact = new RoomContact((RoomChat) abstractChat); + } else { + abstractContact = new ChatContact(abstractChat); + } + if (abstractContact.getName().toLowerCase(locale).contains(filterString)) { + baseEntities.add(abstractContact); + } + } + } + Collections.sort(baseEntities, comparator); + return baseEntities; + } + + @Override + public void run() { + onChange(); + } + + /** + * Listener for contact list appearance changes. + * + * @author alexander.ivanov + */ + public interface OnContactListChangedListener { + + void onContactListChanged(CommonState commonState, boolean hasContacts, + boolean hasVisibleContacts, boolean isFilterEnabled); + + } + + public static class AccountTopSeparator extends BaseEntity { + public AccountTopSeparator(String account, String user) { + super(account, user); + } + } + + public static class AccountBottomSeparator extends BaseEntity { + public AccountBottomSeparator(String account, String user) { + super(account, user); + } + } + + @Override + public Filter getFilter() { + if (contactFilter == null) { + contactFilter = new ContactFilter(); + } + return contactFilter; + } + + private class ContactFilter extends Filter { + + @Override + protected FilterResults performFiltering(CharSequence constraint) { + return null; + } + + @Override + protected void publishResults(CharSequence constraint, + FilterResults results) { + if (constraint == null || constraint.length() == 0) { + filterString = null; + } else { + filterString = constraint.toString().toLowerCase(locale); + } + onChange(); + } + + } + + public boolean isHasActiveChats() { + return hasActiveChats; + } } diff --git a/app/src/main/java/com/xabber/android/ui/adapter/ContactListItemViewHolder.java b/app/src/main/java/com/xabber/android/ui/adapter/ContactListItemViewHolder.java new file mode 100644 index 0000000000..c149ceaae7 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/adapter/ContactListItemViewHolder.java @@ -0,0 +1,36 @@ +package com.xabber.android.ui.adapter; + +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import com.xabber.android.R; + +class ContactListItemViewHolder { + + final ImageView color; + final ImageView avatar; + final TextView name; + final TextView outgoingMessageIndicator; + final TextView secondLineMessage; + final TextView smallRightText; + final ImageView smallRightIcon; + final ImageView largeClientIcon; + final View statusIconSeparator; + final ImageView statusIcon; + final ImageView offlineShadow; + + public ContactListItemViewHolder(View view) { + color = (ImageView) view.findViewById(R.id.account_color_indicator); + avatar = (ImageView) view.findViewById(R.id.avatar); + name = (TextView) view.findViewById(R.id.contact_list_item_name); + outgoingMessageIndicator = (TextView) view.findViewById(R.id.outgoing_message_indicator); + secondLineMessage = (TextView) view.findViewById(R.id.second_line_message); + smallRightIcon = (ImageView) view.findViewById(R.id.small_right_icon); + smallRightText = (TextView) view.findViewById(R.id.small_right_text); + largeClientIcon = (ImageView) view.findViewById(R.id.client_icon_large); + statusIconSeparator = view.findViewById(R.id.status_icon_separator); + statusIcon = (ImageView) view.findViewById(R.id.contact_list_item_status_icon); + offlineShadow = (ImageView) view.findViewById(R.id.offline_shadow); + } +} diff --git a/app/src/main/java/com/xabber/android/ui/adapter/ContactListState.java b/app/src/main/java/com/xabber/android/ui/adapter/ContactListState.java index 80ebf8dc1c..5a75b61664 100644 --- a/app/src/main/java/com/xabber/android/ui/adapter/ContactListState.java +++ b/app/src/main/java/com/xabber/android/ui/adapter/ContactListState.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -19,19 +19,19 @@ */ public enum ContactListState { - /** - * All accounts are offline. - */ - offline, + /** + * All accounts are offline. + */ + offline, - /** - * Connection is in progress. - */ - connecting, + /** + * Connection is in progress. + */ + connecting, - /** - * There is online account. - */ - online, + /** + * There is online account. + */ + online, } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/ui/adapter/GroupConfiguration.java b/app/src/main/java/com/xabber/android/ui/adapter/GroupConfiguration.java index e36b86634f..0faababe82 100644 --- a/app/src/main/java/com/xabber/android/ui/adapter/GroupConfiguration.java +++ b/app/src/main/java/com/xabber/android/ui/adapter/GroupConfiguration.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -30,162 +30,172 @@ */ public class GroupConfiguration extends BaseEntity { - /** - * List of contacts in group. - */ - private final ArrayList abstractContacts; - - /** - * Whether group has no contacts to display in expanded mode. - */ - private boolean empty; - - /** - * Whether group is expanded. - */ - private final boolean expanded; - - /** - * Total number of contacts in group. - */ - private int total; - - /** - * Number of online contacts in group. - */ - private int online; - - /** - * Mode of showing offline contacts. - */ - private final ShowOfflineMode showOfflineMode; - - public GroupConfiguration(String account, String group, - GroupStateProvider groupStateProvider) { - super(account, group); - abstractContacts = new ArrayList(); - expanded = groupStateProvider.isExpanded(account, group); - showOfflineMode = groupStateProvider.getShowOfflineMode(account, group); - empty = true; - total = 0; - online = 0; - } - - /** - * Adds new contact. - * - * @param abstractContact - */ - public void addAbstractContact(AbstractContact abstractContact) { - abstractContacts.add(abstractContact); - } - - /** - * Gets list of contacts. - * - * @return - */ - public Collection getAbstractContacts() { - return abstractContacts; - } - - /** - * Sorts list of abstract contacts. - * - * @param comparator - */ - public void sortAbstractContacts(Comparator comparator) { - Collections.sort(abstractContacts, comparator); - } - - /** - * Increments number of contacts in group. - * - * @param online - * whether contact is online. - */ - public void increment(boolean online) { - this.total++; - if (online) - this.online++; - } - - /** - * @return Whether there is no one contact to be displayed in expanded mode. - */ - public boolean isEmpty() { - return empty; - } - - /** - * Set that there is at least one contact to be displayed in expanded mode. - */ - public void setNotEmpty() { - empty = false; - } - - /** - * @return Whether group is expanded. - */ - public boolean isExpanded() { - return expanded; - } - - /** - * @return Total number of contacts in group. - */ - public int getTotal() { - return total; - } - - /** - * @return Number of online contacts in group. - */ - public int getOnline() { - return online; - } - - /** - * @return Mode of showing offline contacts. - */ - public ShowOfflineMode getShowOfflineMode() { - return showOfflineMode; - } - - @Override - public int compareTo(BaseEntity another) { - final String anotherUser = another.getUser(); - int result = account.compareTo(another.getAccount()); - if (result != 0) { - if (user.compareTo(another.getUser()) != 0) { - if (user == GroupManager.ACTIVE_CHATS) - return -1; - if (anotherUser == GroupManager.ACTIVE_CHATS) - return 1; - } - return result; - } - result = user.compareTo(anotherUser); - if (result != 0) { - if (user == GroupManager.ACTIVE_CHATS) - return -1; - if (anotherUser == GroupManager.ACTIVE_CHATS) - return 1; - if (user == GroupManager.IS_ACCOUNT) - return -1; - if (anotherUser == GroupManager.IS_ACCOUNT) - return 1; - if (user == GroupManager.NO_GROUP) - return -1; - if (anotherUser == GroupManager.NO_GROUP) - return 1; - if (user == GroupManager.IS_ROOM) - return -1; - if (anotherUser == GroupManager.IS_ROOM) - return 1; - return result; - } - return 0; - } + /** + * List of contacts in group. + */ + private final ArrayList abstractContacts; + + /** + * Whether group has no contacts to display in expanded mode. + */ + private boolean empty; + + /** + * Whether group is expanded. + */ + private final boolean expanded; + + /** + * Total number of contacts in group. + */ + private int total; + + /** + * Number of online contacts in group. + */ + private int online; + + /** + * Mode of showing offline contacts. + */ + private final ShowOfflineMode showOfflineMode; + + public GroupConfiguration(String account, String group, + GroupStateProvider groupStateProvider) { + super(account, group); + abstractContacts = new ArrayList<>(); + expanded = groupStateProvider.isExpanded(account, group); + showOfflineMode = groupStateProvider.getShowOfflineMode(account, group); + empty = true; + total = 0; + online = 0; + } + + /** + * Adds new contact. + * + * @param abstractContact + */ + public void addAbstractContact(AbstractContact abstractContact) { + abstractContacts.add(abstractContact); + } + + /** + * Gets list of contacts. + * + * @return + */ + public Collection getAbstractContacts() { + return abstractContacts; + } + + /** + * Sorts list of abstract contacts. + * + * @param comparator + */ + public void sortAbstractContacts(Comparator comparator) { + Collections.sort(abstractContacts, comparator); + } + + /** + * Increments number of contacts in group. + * + * @param online whether contact is online. + */ + public void increment(boolean online) { + this.total++; + if (online) { + this.online++; + } + } + + /** + * @return Whether there is no one contact to be displayed in expanded mode. + */ + public boolean isEmpty() { + return empty; + } + + /** + * Set that there is at least one contact to be displayed in expanded mode. + */ + public void setNotEmpty() { + empty = false; + } + + /** + * @return Whether group is expanded. + */ + public boolean isExpanded() { + return expanded; + } + + /** + * @return Total number of contacts in group. + */ + public int getTotal() { + return total; + } + + /** + * @return Number of online contacts in group. + */ + public int getOnline() { + return online; + } + + /** + * @return Mode of showing offline contacts. + */ + public ShowOfflineMode getShowOfflineMode() { + return showOfflineMode; + } + + @Override + public int compareTo(BaseEntity another) { + final String anotherUser = another.getUser(); + int result = account.compareTo(another.getAccount()); + if (result != 0) { + if (user.compareTo(another.getUser()) != 0) { + if (user.equals(GroupManager.ACTIVE_CHATS)) { + return -1; + } + if (anotherUser.equals(GroupManager.ACTIVE_CHATS)) { + return 1; + } + } + return result; + } + result = user.compareTo(anotherUser); + if (result != 0) { + if (user.equals(GroupManager.ACTIVE_CHATS)) { + return -1; + } + if (anotherUser.equals(GroupManager.ACTIVE_CHATS)) { + return 1; + } + if (user.equals(GroupManager.IS_ACCOUNT)) { + return -1; + } + if (anotherUser.equals(GroupManager.IS_ACCOUNT)) { + return 1; + } + if (user.equals(GroupManager.NO_GROUP)) { + return -1; + } + if (anotherUser.equals(GroupManager.NO_GROUP)) { + return 1; + } + if (user.equals(GroupManager.IS_ROOM)) { + return -1; + } + if (anotherUser.equals(GroupManager.IS_ROOM)) { + return 1; + } + return result; + } + return 0; + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/ui/adapter/GroupEditorAdapter.java b/app/src/main/java/com/xabber/android/ui/adapter/GroupEditorAdapter.java new file mode 100644 index 0000000000..8a932f1bd7 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/adapter/GroupEditorAdapter.java @@ -0,0 +1,84 @@ +package com.xabber.android.ui.adapter; + +import android.support.v4.app.FragmentActivity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.CheckBox; +import android.widget.TextView; + +import com.xabber.android.R; + +import java.util.List; + + +public class GroupEditorAdapter extends ArrayAdapter { + + private FragmentActivity activity; + private int layoutResourceId; + + public GroupEditorAdapter(FragmentActivity activity, int layoutResourceId, List objects) { + super(activity, layoutResourceId, objects); + + this.activity = activity; + this.layoutResourceId = layoutResourceId; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + + View row = convertView; + GroupHolder holder; + + if (row == null) { + LayoutInflater inflater = activity.getLayoutInflater(); + row = inflater.inflate(layoutResourceId, parent, false); + + holder = new GroupHolder(); + holder.groupCheckbox = (CheckBox) row.findViewById(R.id.group_item_selected_checkbox); + holder.groupName = (TextView) row.findViewById(R.id.group_item_name); + + row.setTag(holder); + } else { + holder = (GroupHolder)row.getTag(); + } + + Group group = getItem(position); + + holder.groupName.setText(group.groupName); + holder.groupCheckbox.setChecked(group.isSelected); + + return row; + } + + + + static class GroupHolder { + TextView groupName; + CheckBox groupCheckbox; + } + + public static class Group { + String groupName; + boolean isSelected; + + public Group(String groupName, boolean isSelected) { + this.groupName = groupName; + this.isSelected = isSelected; + } + + public String getGroupName() { + return groupName; + } + + public boolean isSelected() { + return isSelected; + } + + public void setIsSelected(boolean isSelected) { + this.isSelected = isSelected; + } + + } +} diff --git a/app/src/main/java/com/xabber/android/ui/adapter/GroupedContactAdapter.java b/app/src/main/java/com/xabber/android/ui/adapter/GroupedContactAdapter.java index 96839375fe..5f1162627c 100644 --- a/app/src/main/java/com/xabber/android/ui/adapter/GroupedContactAdapter.java +++ b/app/src/main/java/com/xabber/android/ui/adapter/GroupedContactAdapter.java @@ -1,394 +1,575 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.ui.adapter; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.TreeMap; - import android.app.Activity; import android.content.Context; -import android.content.res.ColorStateList; -import android.content.res.TypedArray; +import android.content.res.Resources; +import android.graphics.drawable.ColorDrawable; +import android.os.Build; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.BaseAdapter; import android.widget.ImageView; -import android.widget.ListView; import android.widget.TextView; +import com.xabber.android.R; +import com.xabber.android.data.SettingsManager; +import com.xabber.android.data.account.AccountItem; import com.xabber.android.data.account.AccountManager; +import com.xabber.android.data.account.StatusMode; +import com.xabber.android.data.entity.BaseEntity; +import com.xabber.android.data.extension.avatar.AvatarManager; import com.xabber.android.data.roster.AbstractContact; import com.xabber.android.data.roster.Group; import com.xabber.android.data.roster.GroupManager; -import com.xabber.android.data.roster.GroupStateProvider; import com.xabber.android.data.roster.ShowOfflineMode; -import com.xabber.androiddev.R; +import com.xabber.android.ui.ContactViewer; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; /** * Provide grouping implementation for the list of contacts. - * + * * @author alexander.ivanov - * */ -public abstract class GroupedContactAdapter - extends SmoothContactAdapter { - - /** - * List of groups used if contact has no groups. - */ - static final Collection NO_GROUP_LIST; - - /** - * View type used for contact items. - */ - static final int TYPE_CONTACT = 0; - - /** - * View type used for groups and accounts expanders. - */ - static final int TYPE_GROUP = 1; - - static { - Collection groups = new ArrayList(1); - groups.add(new Group() { - @Override - public String getName() { - return GroupManager.NO_GROUP; - } - }); - NO_GROUP_LIST = Collections.unmodifiableCollection(groups); - } - - /** - * Account's color. - */ - private final ColorStateList expanderAccountTextColor; - - /** - * Group's color. - */ - private final ColorStateList expanderGroupTextColor; - - /** - * Group state provider. - */ - final StateProvider groupStateProvider; - - /** - * Layout inflater - */ - private final LayoutInflater layoutInflater; - - public GroupedContactAdapter(Activity activity, ListView listView, - Inflater inflater, StateProvider groupStateProvider) { - super(activity, listView, inflater); - layoutInflater = (LayoutInflater) activity - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - this.groupStateProvider = groupStateProvider; - TypedArray typedArray; - typedArray = activity.getTheme().obtainStyledAttributes( - R.styleable.ContactList); - expanderAccountTextColor = typedArray - .getColorStateList(R.styleable.ContactList_expanderAccountColor); - expanderGroupTextColor = typedArray - .getColorStateList(R.styleable.ContactList_expanderGroupColor); - typedArray.recycle(); - } - - /** - * Returns group state provider. - * - * @return - */ - public StateProvider getGroupStateProvider() { - return groupStateProvider; - } - - @Override - public int getViewTypeCount() { - return 2; - } - - @Override - public int getItemViewType(int position) { - Object object = getItem(position); - if (object instanceof AbstractContact) - return TYPE_CONTACT; - else if (object instanceof GroupConfiguration) - return TYPE_GROUP; - else - throw new IllegalStateException(); - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - if (getItemViewType(position) == TYPE_CONTACT) { - return super.getView(position, convertView, parent); - } else if (getItemViewType(position) == TYPE_GROUP) { - final View view; - final GroupViewHolder viewHolder; - if (convertView == null) { - view = layoutInflater.inflate(R.layout.base_group_item, parent, - false); - TypedArray typedArray = activity - .obtainStyledAttributes(R.styleable.ContactList); - view.setBackgroundDrawable(typedArray - .getDrawable(R.styleable.ContactList_expanderBackground)); - ((ImageView) view.findViewById(R.id.indicator)) - .setImageDrawable(typedArray - .getDrawable(R.styleable.ContactList_expanderIndicator)); - typedArray.recycle(); - - viewHolder = new GroupViewHolder(view); - view.setTag(viewHolder); - } else { - view = convertView; - viewHolder = (GroupViewHolder) view.getTag(); - } - final GroupConfiguration configuration = (GroupConfiguration) getItem(position); - final int level; - if (configuration instanceof AccountConfiguration) { - level = AccountManager.getInstance().getColorLevel( - configuration.getAccount()); - viewHolder.name.setTextColor(expanderAccountTextColor); - } else { - level = AccountManager.getInstance().getColorCount(); - viewHolder.name.setTextColor(expanderGroupTextColor); - } - view.getBackground().setLevel(level); - viewHolder.name.getBackground().setLevel( - configuration.getShowOfflineMode().ordinal()); - final String name = GroupManager.getInstance().getGroupName( - configuration.getAccount(), configuration.getUser()); - viewHolder.name.setText(name + " (" + configuration.getOnline() - + "/" + configuration.getTotal() + ")"); - viewHolder.indicator.setImageLevel(configuration.isExpanded() ? 1 - : 0); - return view; - } else - throw new IllegalStateException(); - } - - /** - * Gets or creates roster group in roster account. - * - * @param accountConfiguration - * @param name - * @return - */ - protected GroupConfiguration getGroupConfiguration( - AccountConfiguration accountConfiguration, String name) { - GroupConfiguration groupConfiguration = accountConfiguration - .getGroupConfiguration(name); - if (groupConfiguration != null) - return groupConfiguration; - groupConfiguration = new GroupConfiguration( - accountConfiguration.getAccount(), name, groupStateProvider); - accountConfiguration.addGroupConfiguration(groupConfiguration); - return groupConfiguration; - } - - /** - * Gets or creates roster group in tree map. - * - * @param groups - * @param name - * @return - */ - protected GroupConfiguration getGroupConfiguration( - TreeMap groups, String name) { - GroupConfiguration groupConfiguration = groups.get(name); - if (groupConfiguration != null) - return groupConfiguration; - groupConfiguration = new GroupConfiguration(GroupManager.NO_ACCOUNT, - name, groupStateProvider); - groups.put(name, groupConfiguration); - return groupConfiguration; - } - - /** - * Adds contact to specified group. - * - * @param abstractContact - * @param group - * @param online - * @param accounts - * @param groups - * @param contacts - * @param showAccounts - * @param showGroups - */ - protected void addContact(AbstractContact abstractContact, String group, - boolean online, TreeMap accounts, - TreeMap groups, - ArrayList contacts, boolean showAccounts, - boolean showGroups) { - if (showAccounts) { - final String account = abstractContact.getAccount(); - final AccountConfiguration accountConfiguration; - accountConfiguration = accounts.get(account); - if (accountConfiguration == null) - return; - if (showGroups) { - GroupConfiguration groupConfiguration = getGroupConfiguration( - accountConfiguration, group); - if (accountConfiguration.isExpanded()) { - groupConfiguration.setNotEmpty(); - if (groupConfiguration.isExpanded()) - groupConfiguration.addAbstractContact(abstractContact); - } - groupConfiguration.increment(online); - } else { - if (accountConfiguration.isExpanded()) - accountConfiguration.addAbstractContact(abstractContact); - } - accountConfiguration.increment(online); - } else { - if (showGroups) { - GroupConfiguration groupConfiguration = getGroupConfiguration( - groups, group); - groupConfiguration.setNotEmpty(); - if (groupConfiguration.isExpanded()) - groupConfiguration.addAbstractContact(abstractContact); - groupConfiguration.increment(online); - } else { - contacts.add(abstractContact); - } - } - } - - /** - * Adds contact to there groups. - * - * @param abstractContact - * @param online - * @param accounts - * @param groups - * @param contacts - * @param showAccounts - * @param showGroups - * @param showOffline - * @return whether contact is visible. - */ - protected boolean addContact(AbstractContact abstractContact, - boolean online, TreeMap accounts, - TreeMap groups, - ArrayList contacts, boolean showAccounts, - boolean showGroups, boolean showOffline) { - boolean hasVisible = false; - if (showAccounts) { - final AccountConfiguration accountConfiguration; - accountConfiguration = accounts.get(abstractContact.getAccount()); - if (accountConfiguration == null) - return hasVisible; - if (showGroups) { - Collection abstractGroups = abstractContact - .getGroups(); - if (abstractGroups.size() == 0) - abstractGroups = NO_GROUP_LIST; - for (Group abstractGroup : abstractGroups) { - GroupConfiguration groupConfiguration = getGroupConfiguration( - accountConfiguration, abstractGroup.getName()); - if (online - || (groupConfiguration.getShowOfflineMode() == ShowOfflineMode.always) - || (accountConfiguration.getShowOfflineMode() == ShowOfflineMode.always && groupConfiguration - .getShowOfflineMode() == ShowOfflineMode.normal) - || (accountConfiguration.getShowOfflineMode() == ShowOfflineMode.normal - && groupConfiguration.getShowOfflineMode() == ShowOfflineMode.normal && showOffline)) { - // ............. group - // ......... | A | N | E - // ....... A | + | + | - - // account N | + | ? | - - // ....... E | + | - | - - hasVisible = true; - if (accountConfiguration.isExpanded()) { - groupConfiguration.setNotEmpty(); - if (groupConfiguration.isExpanded()) - groupConfiguration - .addAbstractContact(abstractContact); - } - } - groupConfiguration.increment(online); - } - } else { - if (online - || (accountConfiguration.getShowOfflineMode() == ShowOfflineMode.always) - || (accountConfiguration.getShowOfflineMode() == ShowOfflineMode.normal && showOffline)) { - hasVisible = true; - if (accountConfiguration.isExpanded()) - accountConfiguration - .addAbstractContact(abstractContact); - } - } - accountConfiguration.increment(online); - } else { - if (showGroups) { - Collection abstractGroups = abstractContact - .getGroups(); - if (abstractGroups.size() == 0) - abstractGroups = NO_GROUP_LIST; - for (Group abstractGroup : abstractGroups) { - GroupConfiguration groupConfiguration = getGroupConfiguration( - groups, abstractGroup.getName()); - if (online - || (groupConfiguration.getShowOfflineMode() == ShowOfflineMode.always) - || (groupConfiguration.getShowOfflineMode() == ShowOfflineMode.normal && showOffline)) { - groupConfiguration.setNotEmpty(); - hasVisible = true; - if (groupConfiguration.isExpanded()) - groupConfiguration - .addAbstractContact(abstractContact); - } - groupConfiguration.increment(online); - } - } else { - if (online || showOffline) { - hasVisible = true; - contacts.add(abstractContact); - } - } - } - return hasVisible; - } - - /** - * Sets whether group in specified account is expanded. - * - * @param account - * @param group - * Use {@link #IS_ACCOUNT} to set expanded for account. - * @param expanded - */ - public void setExpanded(String account, String group, boolean expanded) { - groupStateProvider.setExpanded(account, group, expanded); - onChange(); - } - - /** - * Holder for views in contact list group. - */ - private static class GroupViewHolder { - final ImageView indicator; - final TextView name; - - public GroupViewHolder(View view) { - indicator = (ImageView) view.findViewById(R.id.indicator); - name = (TextView) view.findViewById(R.id.name); - } - } +public abstract class GroupedContactAdapter extends BaseAdapter implements UpdatableAdapter { + + /** + * List of groups used if contact has no groups. + */ + static final Collection NO_GROUP_LIST; + + static final int TYPE_COUNT = 5; + + /** + * View type used for contact items. + */ + static final int TYPE_CONTACT = 0; + + /** + * View type used for groups and accounts expanders. + */ + static final int TYPE_GROUP = 1; + static final int TYPE_ACCOUNT = 2; + static final int TYPE_ACCOUNT_TOP_SEPARATOR = 3; + static final int TYPE_ACCOUNT_BOTTOM_SEPARATOR = 4; + + static { + Collection groups = new ArrayList<>(1); + groups.add(new Group() { + @Override + public String getName() { + return GroupManager.NO_GROUP; + } + }); + NO_GROUP_LIST = Collections.unmodifiableCollection(groups); + } + + final ArrayList baseEntities = new ArrayList<>(); + /** + * Layout inflater + */ + private final LayoutInflater layoutInflater; + private final Activity activity; + private final int[] accountSubgroupColors; + private final int activeChatsColor; + private final OnClickListener onClickListener; + private final ContactItemInflater contactItemInflater; + private final int accountElevation; + protected Locale locale = Locale.getDefault(); + private int[] accountGroupColors; + + public GroupedContactAdapter(Activity activity, OnClickListener onClickListener) { + this.activity = activity; + + layoutInflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + Resources resources = activity.getResources(); + + + accountGroupColors = resources.getIntArray(R.array.account_200); + accountSubgroupColors = resources.getIntArray(R.array.account_50); + activeChatsColor = resources.getColor(R.color.contact_list_active_chats_group_background); + + contactItemInflater = new ContactItemInflater(activity); + + accountElevation = activity.getResources().getDimensionPixelSize(R.dimen.account_group_elevation); + + this.onClickListener = onClickListener; + } + + @Override + public void onChange() { + notifyDataSetChanged(); + } + + @Override + public int getCount() { + return baseEntities.size(); + } + + @Override + public Object getItem(int position) { + return baseEntities.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public int getViewTypeCount() { + return TYPE_COUNT; + } + + @Override + public int getItemViewType(int position) { + Object object = getItem(position); + if (object instanceof AbstractContact) { + return TYPE_CONTACT; + } else if (object instanceof AccountConfiguration) { + return TYPE_ACCOUNT; + } else if (object instanceof GroupConfiguration) { + return TYPE_GROUP; + } else if (object instanceof ContactListAdapter.AccountTopSeparator) { + return TYPE_ACCOUNT_TOP_SEPARATOR; + } else if (object instanceof ContactListAdapter.AccountBottomSeparator) { + return TYPE_ACCOUNT_BOTTOM_SEPARATOR; + } else { + throw new IllegalStateException(); + } + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + switch (getItemViewType(position)) { + case TYPE_CONTACT: + return getContactView(position, convertView, parent); + + case TYPE_GROUP: + return getGroupView(position, convertView, parent); + + case TYPE_ACCOUNT: + return getAccountView(position, convertView, parent); + + case TYPE_ACCOUNT_TOP_SEPARATOR: + return getView(convertView, parent, R.layout.account_group_item_top_separator); + + case TYPE_ACCOUNT_BOTTOM_SEPARATOR: + return getAccountBottomSeparatorView( + getView(convertView, parent, R.layout.account_group_item_bottom_separator), position); + + default: + throw new IllegalStateException(); + } + } + + private View getView(View convertView, ViewGroup parent, int layoutId) { + final View view; + if (convertView == null) { + view = layoutInflater.inflate(layoutId, parent, false); + } else { + view = convertView; + } + return view; + } + + private View getAccountBottomSeparatorView(View view, int position) { + final ContactListAdapter.AccountBottomSeparator accountBottomSeparator + = (ContactListAdapter.AccountBottomSeparator) getItem(position); + final int level = AccountManager.getInstance().getColorLevel(accountBottomSeparator.getAccount()); + + View bottomLayer = view.findViewById(R.id.bottom_layer); + View topLayer = view.findViewById(R.id.top_layer); + + bottomLayer.setBackgroundDrawable(new ColorDrawable(accountSubgroupColors[level])); + topLayer.setBackgroundDrawable(new ColorDrawable(accountSubgroupColors[level])); + + StatusMode statusMode = AccountManager.getInstance().getAccount(accountBottomSeparator.getAccount()).getDisplayStatusMode(); + + View offlineShadowBottom = view.findViewById(R.id.offline_shadow_top); + View offlineShadowTop = view.findViewById(R.id.offline_shadow_bottom); + + if (statusMode == StatusMode.unavailable || statusMode == StatusMode.connection) { + offlineShadowBottom.setVisibility(View.VISIBLE); + offlineShadowTop.setVisibility(View.VISIBLE); + } else { + offlineShadowBottom.setVisibility(View.GONE); + offlineShadowTop.setVisibility(View.GONE); + } + + return view; + } + + private View getAccountView(int position, View convertView, ViewGroup parent) { + final View view; + final ContactListItemViewHolder viewHolder; + if (convertView == null) { + view = layoutInflater.inflate(R.layout.contact_list_item, parent, false); + + viewHolder = new ContactListItemViewHolder(view); + viewHolder.outgoingMessageIndicator.setVisibility(View.GONE); + viewHolder.color.setVisibility(View.INVISIBLE); + viewHolder.largeClientIcon.setVisibility(View.GONE); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + view.setElevation(accountElevation); + } + view.setTag(viewHolder); + } else { + view = convertView; + viewHolder = (ContactListItemViewHolder) view.getTag(); + } + + final AccountConfiguration configuration = (AccountConfiguration) getItem(position); + + final String account = configuration.getAccount(); + + viewHolder.statusIcon.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onClickListener.onAccountMenuClick(v, account); + } + }); + + final int level = AccountManager.getInstance().getColorLevel(account); + view.setBackgroundDrawable(new ColorDrawable(accountGroupColors[level])); + + viewHolder.name.setText(GroupManager.getInstance().getGroupName(account, configuration.getUser())); + viewHolder.smallRightText.setText(configuration.getOnline() + "/" + configuration.getTotal()); + + AccountItem accountItem = AccountManager.getInstance().getAccount(account); + String statusText = accountItem.getStatusText().trim(); + + if (statusText.isEmpty()) { + statusText = activity.getString(accountItem.getDisplayStatusMode().getStringID()); + } + + viewHolder.secondLineMessage.setText(statusText); + + if (SettingsManager.contactsShowAvatars()) { + viewHolder.avatar.setVisibility(View.VISIBLE); + viewHolder.avatar.setImageDrawable(AvatarManager.getInstance().getAccountAvatar(account)); + } else { + viewHolder.avatar.setVisibility(View.GONE); + } + + viewHolder.avatar.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + activity.startActivity(ContactViewer.createIntent(activity, account, GroupManager.IS_ACCOUNT)); + } + }); + + viewHolder.statusIcon.setImageLevel(accountItem.getDisplayStatusMode().getStatusLevel()); + + ShowOfflineMode showOfflineMode = configuration.getShowOfflineMode(); + if (showOfflineMode == ShowOfflineMode.normal) { + if (SettingsManager.contactsShowOffline()) { + showOfflineMode = ShowOfflineMode.always; + } else { + showOfflineMode = ShowOfflineMode.never; + } + } + + viewHolder.smallRightIcon.setImageLevel(showOfflineMode.ordinal()); + + + StatusMode statusMode = AccountManager.getInstance().getAccount(configuration.getAccount()).getDisplayStatusMode(); + + if (statusMode == StatusMode.unavailable || statusMode == StatusMode.connection) { + viewHolder.offlineShadow.setVisibility(View.VISIBLE); + } else { + viewHolder.offlineShadow.setVisibility(View.GONE); + } + + return view; + } + + private View getGroupView(int position, View convertView, ViewGroup parent) { + final View view; + final GroupViewHolder viewHolder; + if (convertView == null) { + view = layoutInflater.inflate(R.layout.base_group_item, parent, false); + viewHolder = new GroupViewHolder(view); + view.setTag(viewHolder); + } else { + view = convertView; + viewHolder = (GroupViewHolder) view.getTag(); + } + + final GroupConfiguration configuration = (GroupConfiguration) getItem(position); + final int level = AccountManager.getInstance().getColorLevel(configuration.getAccount()); + + final String name = GroupManager.getInstance() + .getGroupName(configuration.getAccount(), configuration.getUser()); + + + viewHolder.indicator.setImageLevel(configuration.isExpanded() ? 1 : 0); + viewHolder.groupOfflineIndicator.setImageLevel(configuration.getShowOfflineMode().ordinal()); + + int color; + + viewHolder.groupOfflineIndicator.setVisibility(View.GONE); + viewHolder.offlineShadow.setVisibility(View.GONE); + + if (configuration.getUser().equals(GroupManager.ACTIVE_CHATS)) { + color = activeChatsColor; + viewHolder.name.setText(name); + } else { + viewHolder.name.setText(name + " (" + configuration.getOnline() + + "/" + configuration.getTotal() + ")"); + + color = accountSubgroupColors[level]; + viewHolder.groupOfflineIndicator.setVisibility(View.VISIBLE); + + AccountItem accountItem = AccountManager.getInstance().getAccount(configuration.getAccount()); + + if (accountItem != null) { + StatusMode statusMode = accountItem.getDisplayStatusMode(); + if (statusMode == StatusMode.unavailable || statusMode == StatusMode.connection) { + viewHolder.offlineShadow.setVisibility(View.VISIBLE); + } + } + } + + view.setBackgroundDrawable(new ColorDrawable(color)); + + return view; + } + + private View getContactView(int position, View convertView, ViewGroup parent) { + final AbstractContact abstractContact = (AbstractContact) getItem(position); + return contactItemInflater.setUpContactView(convertView, parent, abstractContact); + } + + /** + * Gets or creates roster group in roster account. + * + * @param accountConfiguration + * @param name + * @return + */ + protected GroupConfiguration getGroupConfiguration(AccountConfiguration accountConfiguration, String name) { + GroupConfiguration groupConfiguration = accountConfiguration.getGroupConfiguration(name); + if (groupConfiguration != null) { + return groupConfiguration; + } + groupConfiguration = new GroupConfiguration( + accountConfiguration.getAccount(), name, GroupManager.getInstance()); + accountConfiguration.addGroupConfiguration(groupConfiguration); + return groupConfiguration; + } + + /** + * Gets or creates roster group in tree map. + * + * @param groups + * @param name + * @return + */ + protected GroupConfiguration getGroupConfiguration(Map groups, String name) { + GroupConfiguration groupConfiguration = groups.get(name); + if (groupConfiguration != null) { + return groupConfiguration; + } + groupConfiguration = new GroupConfiguration(GroupManager.NO_ACCOUNT, name, GroupManager.getInstance()); + groups.put(name, groupConfiguration); + return groupConfiguration; + } + + /** + * Adds contact to specified group. + * + * @param abstractContact + * @param group + * @param online + * @param accounts + * @param groups + * @param contacts + * @param showAccounts + * @param showGroups + */ + protected void addContact(AbstractContact abstractContact, String group, boolean online, + Map accounts, Map groups, + List contacts, boolean showAccounts, boolean showGroups) { + if (showAccounts) { + final String account = abstractContact.getAccount(); + final AccountConfiguration accountConfiguration; + accountConfiguration = accounts.get(account); + if (accountConfiguration == null) { + return; + } + if (showGroups) { + GroupConfiguration groupConfiguration + = getGroupConfiguration(accountConfiguration, group); + if (accountConfiguration.isExpanded()) { + groupConfiguration.setNotEmpty(); + if (groupConfiguration.isExpanded()) { + groupConfiguration.addAbstractContact(abstractContact); + } + } + groupConfiguration.increment(online); + } else { + if (accountConfiguration.isExpanded()) { + accountConfiguration.addAbstractContact(abstractContact); + } + } + accountConfiguration.increment(online); + } else { + if (showGroups) { + GroupConfiguration groupConfiguration = getGroupConfiguration(groups, group); + groupConfiguration.setNotEmpty(); + if (groupConfiguration.isExpanded()) { + groupConfiguration.addAbstractContact(abstractContact); + } + groupConfiguration.increment(online); + } else { + contacts.add(abstractContact); + } + } + } + + /** + * Adds contact to there groups. + * + * @param abstractContact + * @param online + * @param accounts + * @param groups + * @param contacts + * @param showAccounts + * @param showGroups + * @param showOffline + * @return whether contact is visible. + */ + protected boolean addContact(AbstractContact abstractContact, + boolean online, Map accounts, + Map groups, + List contacts, boolean showAccounts, + boolean showGroups, boolean showOffline) { + boolean hasVisible = false; + if (showAccounts) { + final AccountConfiguration accountConfiguration; + accountConfiguration = accounts.get(abstractContact.getAccount()); + if (accountConfiguration == null) { + return false; + } + if (showGroups) { + Collection abstractGroups = abstractContact.getGroups(); + if (abstractGroups.size() == 0) { + abstractGroups = NO_GROUP_LIST; + } + for (Group abstractGroup : abstractGroups) { + GroupConfiguration groupConfiguration = getGroupConfiguration( + accountConfiguration, abstractGroup.getName()); + if (online + || (groupConfiguration.getShowOfflineMode() == ShowOfflineMode.always) + || (accountConfiguration.getShowOfflineMode() == ShowOfflineMode.always && groupConfiguration + .getShowOfflineMode() == ShowOfflineMode.normal) + || (accountConfiguration.getShowOfflineMode() == ShowOfflineMode.normal + && groupConfiguration.getShowOfflineMode() == ShowOfflineMode.normal && showOffline)) { + // ............. group + // ......... | A | N | E + // ....... A | + | + | - + // account N | + | ? | - + // ....... E | + | - | - + hasVisible = true; + if (accountConfiguration.isExpanded()) { + groupConfiguration.setNotEmpty(); + if (groupConfiguration.isExpanded()) { + groupConfiguration.addAbstractContact(abstractContact); + } + } + } + groupConfiguration.increment(online); + } + } else { + if (online || (accountConfiguration.getShowOfflineMode() == ShowOfflineMode.always) + || (accountConfiguration.getShowOfflineMode() == ShowOfflineMode.normal && showOffline)) { + hasVisible = true; + if (accountConfiguration.isExpanded()) { + accountConfiguration.addAbstractContact(abstractContact); + } + } + } + accountConfiguration.increment(online); + } else { + if (showGroups) { + Collection abstractGroups = abstractContact.getGroups(); + if (abstractGroups.size() == 0) { + abstractGroups = NO_GROUP_LIST; + } + for (Group abstractGroup : abstractGroups) { + GroupConfiguration groupConfiguration + = getGroupConfiguration(groups, abstractGroup.getName()); + if (online || (groupConfiguration.getShowOfflineMode() == ShowOfflineMode.always) + || (groupConfiguration.getShowOfflineMode() == ShowOfflineMode.normal && showOffline)) { + groupConfiguration.setNotEmpty(); + hasVisible = true; + if (groupConfiguration.isExpanded()) { + groupConfiguration.addAbstractContact(abstractContact); + } + } + groupConfiguration.increment(online); + } + } else { + if (online || showOffline) { + hasVisible = true; + contacts.add(abstractContact); + } + } + } + return hasVisible; + } + + /** + * Sets whether group in specified account is expanded. + */ + public void setExpanded(String account, String group, boolean expanded) { + GroupManager.getInstance().setExpanded(account, group, expanded); + onChange(); + } + + public interface OnClickListener { + void onAccountMenuClick(View view, String account); + } + + /** + * Holder for views in contact list group. + */ + private static class GroupViewHolder { + final ImageView indicator; + final TextView name; + final ImageView groupOfflineIndicator; + final ImageView offlineShadow; + + public GroupViewHolder(View view) { + indicator = (ImageView) view.findViewById(R.id.indicator); + name = (TextView) view.findViewById(R.id.name); + groupOfflineIndicator = (ImageView) view.findViewById(R.id.group_offline_indicator); + offlineShadow = (ImageView) view.findViewById(R.id.offline_shadow); + } + } } diff --git a/app/src/main/java/com/xabber/android/ui/adapter/NavigationDrawerAccountAdapter.java b/app/src/main/java/com/xabber/android/ui/adapter/NavigationDrawerAccountAdapter.java new file mode 100644 index 0000000000..845822d87b --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/adapter/NavigationDrawerAccountAdapter.java @@ -0,0 +1,72 @@ +package com.xabber.android.ui.adapter; + +import android.app.Activity; +import android.graphics.drawable.ColorDrawable; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import com.xabber.android.R; +import com.xabber.android.data.account.AccountItem; +import com.xabber.android.data.account.AccountManager; +import com.xabber.android.data.connection.ConnectionState; +import com.xabber.android.data.extension.avatar.AvatarManager; +import com.xabber.android.data.roster.RosterManager; +import com.xabber.android.ui.helper.AccountPainter; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + + +public class NavigationDrawerAccountAdapter extends BaseListEditorAdapter { + private final AccountPainter accountPainter; + + public NavigationDrawerAccountAdapter(Activity activity) { + super(activity); + accountPainter = new AccountPainter(activity); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View view; + AccountManager accountManager = AccountManager.getInstance(); + if (convertView == null) { + view = getActivity().getLayoutInflater().inflate(R.layout.contact_list_drawer_account_item, parent, false); + } else { + view = convertView; + } + String account = getItem(position); + + ((ImageView) view.findViewById(R.id.color)).setImageDrawable(new ColorDrawable((accountPainter.getAccountMainColor(account)))); + ((ImageView) view.findViewById(R.id.avatar)).setImageDrawable(AvatarManager.getInstance().getAccountAvatar(account)); + + TextView accountName = (TextView) view.findViewById(R.id.name); + + accountName.setText(RosterManager.getInstance().getBestContact(account, accountManager.getVerboseName(account)).getName()); + accountName.setTextColor(accountPainter.getAccountDarkestColor(account)); + + ((TextView) view.findViewById(R.id.account_jid)).setText(accountManager.getVerboseName(account)); + + AccountItem accountItem = accountManager.getAccount(account); + ConnectionState state; + if (accountItem == null) { + state = ConnectionState.offline; + } else { + state = accountItem.getState(); + } + ((TextView) view.findViewById(R.id.status)).setText(getActivity().getString(state.getStringId())); + return view; + } + + @Override + protected Collection getTags() { + List list = new ArrayList<>(); + list.addAll(AccountManager.getInstance().getAccounts()); + Collections.sort(list); + return list; + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/xabber/android/ui/adapter/OccupantListAdapter.java b/app/src/main/java/com/xabber/android/ui/adapter/OccupantListAdapter.java index 514a9f2fdb..bb721fd174 100644 --- a/app/src/main/java/com/xabber/android/ui/adapter/OccupantListAdapter.java +++ b/app/src/main/java/com/xabber/android/ui/adapter/OccupantListAdapter.java @@ -1,22 +1,19 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.ui.adapter; -import java.util.ArrayList; -import java.util.Collections; - import android.app.Activity; import android.view.View; import android.view.ViewGroup; @@ -24,95 +21,97 @@ import android.widget.ImageView; import android.widget.TextView; +import com.xabber.android.R; import com.xabber.android.data.extension.avatar.AvatarManager; import com.xabber.android.data.extension.muc.MUCManager; import com.xabber.android.data.extension.muc.Occupant; import com.xabber.android.ui.OccupantList; -import com.xabber.androiddev.R; import com.xabber.xmpp.muc.Role; +import java.util.ArrayList; +import java.util.Collections; + /** * Adapter for {@link OccupantList}. - * + * * @author alexander.ivanov - * */ public class OccupantListAdapter extends BaseAdapter implements - UpdatableAdapter { + UpdatableAdapter { - private final Activity activity; - private final String account; - private final String room; + private final Activity activity; + private final String account; + private final String room; - private final ArrayList occupants; + private final ArrayList occupants; - public OccupantListAdapter(Activity activity, String account, String room) { - this.activity = activity; - this.account = account; - this.room = room; - occupants = new ArrayList(); - } + public OccupantListAdapter(Activity activity, String account, String room) { + this.activity = activity; + this.account = account; + this.room = room; + occupants = new ArrayList(); + } - @Override - public void onChange() { - occupants.clear(); - occupants.addAll(MUCManager.getInstance().getOccupants(account, room)); - Collections.sort(occupants); - notifyDataSetChanged(); - } + @Override + public void onChange() { + occupants.clear(); + occupants.addAll(MUCManager.getInstance().getOccupants(account, room)); + Collections.sort(occupants); + notifyDataSetChanged(); + } - @Override - public int getCount() { - return occupants.size(); - } + @Override + public int getCount() { + return occupants.size(); + } - @Override - public Object getItem(int position) { - return occupants.get(position); - } + @Override + public Object getItem(int position) { + return occupants.get(position); + } - @Override - public long getItemId(int position) { - return position; - } + @Override + public long getItemId(int position) { + return position; + } - @Override - public View getView(int position, View convertView, ViewGroup parent) { - final View view; - if (convertView == null) { - view = activity.getLayoutInflater().inflate( - R.layout.occupant_list_item, parent, false); - } else { - view = convertView; - } - final Occupant occupant = (Occupant) getItem(position); - final ImageView avatarView = (ImageView) view.findViewById(R.id.avatar); - final ImageView affilationView = (ImageView) view - .findViewById(R.id.affilation); - final TextView nameView = (TextView) view.findViewById(R.id.name); - final TextView statusTextView = (TextView) view - .findViewById(R.id.status); - final ImageView statusModeView = (ImageView) view - .findViewById(R.id.status_mode); - if (MUCManager.getInstance().getNickname(account, room) - .equalsIgnoreCase(occupant.getNickname())) - avatarView.setImageDrawable(AvatarManager.getInstance() - .getAccountAvatar(account)); - else - avatarView.setImageDrawable(AvatarManager.getInstance() - .getOccupantAvatar(room + "/" + occupant.getNickname())); - affilationView.setImageLevel(occupant.getAffiliation().ordinal()); - nameView.setText(occupant.getNickname()); - int textStyle; - if (occupant.getRole() == Role.moderator) - textStyle = R.style.OccupantList_Moderator; - else if (occupant.getRole() == Role.participant) - textStyle = R.style.OccupantList_Participant; - else - textStyle = R.style.OccupantList_Visitor; - nameView.setTextAppearance(activity, textStyle); - statusTextView.setText(occupant.getStatusText()); - statusModeView.setImageLevel(occupant.getStatusMode().getStatusLevel()); - return view; - } + @Override + public View getView(int position, View convertView, ViewGroup parent) { + final View view; + if (convertView == null) { + view = activity.getLayoutInflater().inflate( + R.layout.occupant_list_item, parent, false); + } else { + view = convertView; + } + final Occupant occupant = (Occupant) getItem(position); + final ImageView avatarView = (ImageView) view.findViewById(R.id.avatar); + final ImageView affilationView = (ImageView) view + .findViewById(R.id.affilation); + final TextView nameView = (TextView) view.findViewById(R.id.name); + final TextView statusTextView = (TextView) view + .findViewById(R.id.status); + final ImageView statusModeView = (ImageView) view + .findViewById(R.id.status_icon); + if (MUCManager.getInstance().getNickname(account, room) + .equalsIgnoreCase(occupant.getNickname())) + avatarView.setImageDrawable(AvatarManager.getInstance() + .getAccountAvatar(account)); + else + avatarView.setImageDrawable(AvatarManager.getInstance() + .getOccupantAvatar(room + "/" + occupant.getNickname())); + affilationView.setImageLevel(occupant.getAffiliation().ordinal()); + nameView.setText(occupant.getNickname()); + int textStyle; + if (occupant.getRole() == Role.moderator) + textStyle = R.style.OccupantList_Moderator; + else if (occupant.getRole() == Role.participant) + textStyle = R.style.OccupantList_Participant; + else + textStyle = R.style.OccupantList_Visitor; + nameView.setTextAppearance(activity, textStyle); + statusTextView.setText(occupant.getStatusText()); + statusModeView.setImageLevel(occupant.getStatusMode().getStatusLevel()); + return view; + } } diff --git a/app/src/main/java/com/xabber/android/ui/adapter/OnTextChangedListener.java b/app/src/main/java/com/xabber/android/ui/adapter/OnTextChangedListener.java deleted file mode 100644 index 4a1e89e7fc..0000000000 --- a/app/src/main/java/com/xabber/android/ui/adapter/OnTextChangedListener.java +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.xabber.android.ui.adapter; - -import android.widget.EditText; - -/** - * Listener for text to be changed. - * - * @author alexander.ivanov - * - */ -public interface OnTextChangedListener { - - void onTextChanged(EditText editText, CharSequence text); - -} diff --git a/app/src/main/java/com/xabber/android/ui/adapter/PhraseListAdapter.java b/app/src/main/java/com/xabber/android/ui/adapter/PhraseListAdapter.java index 4edd4daa11..6332059e28 100644 --- a/app/src/main/java/com/xabber/android/ui/adapter/PhraseListAdapter.java +++ b/app/src/main/java/com/xabber/android/ui/adapter/PhraseListAdapter.java @@ -1,82 +1,81 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.ui.adapter; -import java.util.Collection; - import android.app.Activity; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; +import com.xabber.android.R; import com.xabber.android.data.message.phrase.Phrase; import com.xabber.android.data.message.phrase.PhraseManager; -import com.xabber.androiddev.R; + +import java.util.Collection; /** * This class manage list of phrases. - * + * * @author alexander.ivanov - * */ public class PhraseListAdapter extends BaseListEditorAdapter { - public PhraseListAdapter(Activity activity) { - super(activity); - } + public PhraseListAdapter(Activity activity) { + super(activity); + } - private String append(String message, String value) { - if (!"".equals(message)) - message += "\n"; - return message + value; - } + private String append(String message, String value) { + if (!"".equals(message)) + message += "\n"; + return message + value; + } - @Override - public View getView(int position, View convertView, ViewGroup parent) { - View view; - if (convertView == null) { - view = getActivity().getLayoutInflater().inflate( - R.layout.preference, parent, false); - } else { - view = convertView; - } - Phrase phrase = PhraseManager.getInstance() - .getPhrase(getItem(position)); - String title = phrase.getText(); - if ("".equals(title)) - title = getActivity().getString(R.string.phrase_empty); - String message = ""; - if (!"".equals(phrase.getUser())) - message = append(message, - getActivity().getString(R.string.phrase_user) + ": " - + phrase.getUser()); - if (!"".equals(phrase.getGroup())) - message = append(message, - getActivity().getString(R.string.phrase_group) + ": " - + phrase.getGroup()); - if (phrase.isRegexp()) - message = append(message, - getActivity().getString(R.string.phrase_regexp)); - ((TextView) view.findViewById(android.R.id.title)).setText(title); - ((TextView) view.findViewById(android.R.id.summary)).setText(message); - return view; - } + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View view; + if (convertView == null) { + view = getActivity().getLayoutInflater().inflate( + R.layout.preference, parent, false); + } else { + view = convertView; + } + Phrase phrase = PhraseManager.getInstance() + .getPhrase(getItem(position)); + String title = phrase.getText(); + if ("".equals(title)) + title = getActivity().getString(R.string.phrase_empty); + String message = ""; + if (!"".equals(phrase.getUser())) + message = append(message, + getActivity().getString(R.string.phrase_user) + ": " + + phrase.getUser()); + if (!"".equals(phrase.getGroup())) + message = append(message, + getActivity().getString(R.string.phrase_group) + ": " + + phrase.getGroup()); + if (phrase.isRegexp()) + message = append(message, + getActivity().getString(R.string.phrase_regexp)); + ((TextView) view.findViewById(android.R.id.title)).setText(title); + ((TextView) view.findViewById(android.R.id.summary)).setText(message); + return view; + } - @Override - protected Collection getTags() { - return PhraseManager.getInstance().getPhrases(); - } + @Override + protected Collection getTags() { + return PhraseManager.getInstance().getPhrases(); + } } diff --git a/app/src/main/java/com/xabber/android/ui/adapter/SaveStateAdapter.java b/app/src/main/java/com/xabber/android/ui/adapter/SaveStateAdapter.java deleted file mode 100644 index c2e69de5ee..0000000000 --- a/app/src/main/java/com/xabber/android/ui/adapter/SaveStateAdapter.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.xabber.android.ui.adapter; - -import android.view.View; -import android.widget.Adapter; - -/** - * Adapter that can save state of view on activity paused or on element removed - * from layout. - * - * Warning: This interface is to be removed. - * - * @author alexander.ivanov - * - */ -public interface SaveStateAdapter extends Adapter { - - /** - * Will be called before view will replaced with another or before activity - * will be paused. - * - * @param view - */ - void saveState(View view); - -} diff --git a/app/src/main/java/com/xabber/android/ui/adapter/SmoothContactAdapter.java b/app/src/main/java/com/xabber/android/ui/adapter/SmoothContactAdapter.java deleted file mode 100644 index 7b92d0202c..0000000000 --- a/app/src/main/java/com/xabber/android/ui/adapter/SmoothContactAdapter.java +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.xabber.android.ui.adapter; - -import android.app.Activity; -import android.widget.ListView; - -/** - * Enable smooth scrollbar depend on number of entities. - * - * @author alexander.ivanov - * - * @param - */ -public abstract class SmoothContactAdapter - extends BaseContactAdapter { - - /** - * Minimum number of item when smooth scroll bar will be disabled. - */ - private static final int SMOOTH_SCROLLBAR_LIMIT = 20; - - /** - * Managed list view. - */ - ListView listView; - - public SmoothContactAdapter(Activity activity, ListView listView, - Inflater inflater) { - super(activity, inflater); - this.listView = listView; - } - - @Override - public void onChange() { - super.onChange(); - listView.setSmoothScrollbarEnabled(baseEntities.size() < SMOOTH_SCROLLBAR_LIMIT); - } - -} diff --git a/app/src/main/java/com/xabber/android/ui/adapter/StatusContactInflater.java b/app/src/main/java/com/xabber/android/ui/adapter/StatusContactInflater.java deleted file mode 100644 index f3d49b7212..0000000000 --- a/app/src/main/java/com/xabber/android/ui/adapter/StatusContactInflater.java +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.xabber.android.ui.adapter; - -import android.app.Activity; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; - -import com.xabber.android.data.roster.AbstractContact; -import com.xabber.androiddev.R; - -/** - * Inflate view with contact's status mode. - * - * @author alexander.ivanov - * - */ -public class StatusContactInflater extends BaseContactInflater { - - public StatusContactInflater(Activity activity) { - super(activity); - } - - @Override - View createView(int position, ViewGroup parent) { - return layoutInflater - .inflate(R.layout.base_contact_item, parent, false); - } - - @Override - ViewHolder createViewHolder(int position, View view) { - return new StatusContactInflater.ViewHolder(view); - } - - @Override - public void getView(View view, AbstractContact abstractContact) { - super.getView(view, abstractContact); - ViewHolder viewHolder = (ViewHolder) view.getTag(); - viewHolder.statusMode.setImageLevel(abstractContact.getStatusMode() - .getStatusLevel()); - } - - static class ViewHolder extends BaseContactInflater.ViewHolder { - - final ImageView statusMode; - - public ViewHolder(View view) { - super(view); - statusMode = (ImageView) view.findViewById(R.id.status_mode); - } - } - -} diff --git a/app/src/main/java/com/xabber/android/ui/adapter/StatusEditorAdapter.java b/app/src/main/java/com/xabber/android/ui/adapter/StatusEditorAdapter.java index f7184eb841..1143b22795 100644 --- a/app/src/main/java/com/xabber/android/ui/adapter/StatusEditorAdapter.java +++ b/app/src/main/java/com/xabber/android/ui/adapter/StatusEditorAdapter.java @@ -1,22 +1,19 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.ui.adapter; -import java.util.ArrayList; -import java.util.Collections; - import android.app.Activity; import android.view.View; import android.view.ViewGroup; @@ -24,67 +21,67 @@ import android.widget.ImageView; import android.widget.TextView; +import com.xabber.android.R; import com.xabber.android.data.account.AccountManager; import com.xabber.android.data.account.SavedStatus; -import com.xabber.androiddev.R; + +import java.util.ArrayList; +import java.util.Collections; /** * Adapter for saved statuses. - * + * * @author alexander.ivanov - * */ -public class StatusEditorAdapter extends BaseAdapter implements - UpdatableAdapter { +public class StatusEditorAdapter extends BaseAdapter implements UpdatableAdapter { - private final Activity activity; - private final ArrayList statuses; + private final Activity activity; + private final ArrayList statuses; - public StatusEditorAdapter(Activity activity) { - super(); - this.activity = activity; - statuses = new ArrayList(); - } + public StatusEditorAdapter(Activity activity) { + super(); + this.activity = activity; + statuses = new ArrayList<>(); + } - @Override - public int getCount() { - return statuses.size(); - } + @Override + public int getCount() { + return statuses.size(); + } - @Override - public Object getItem(int position) { - return statuses.get(position); - } + @Override + public Object getItem(int position) { + return statuses.get(position); + } - @Override - public long getItemId(int position) { - return position; - } + @Override + public long getItemId(int position) { + return position; + } - @Override - public View getView(int position, View convertView, ViewGroup parent) { - View view; - if (convertView == null) { - view = activity.getLayoutInflater().inflate( - R.layout.status_editor_item, parent, false); - } else { - view = convertView; - } - final SavedStatus status = (SavedStatus) getItem(position); - ((ImageView) view.findViewById(R.id.icon)).setImageLevel(status - .getStatusMode().getStatusLevel()); - String text = status.getStatusText(); - if ("".equals(text)) - text = activity.getString(R.string.empty_status); - ((TextView) view.findViewById(R.id.name)).setText(text); - return view; - } + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View view; + if (convertView == null) { + view = activity.getLayoutInflater().inflate(R.layout.status_editor_item, parent, false); + } else { + view = convertView; + } + final SavedStatus status = (SavedStatus) getItem(position); + ((ImageView) view.findViewById(R.id.icon)).setImageLevel(status.getStatusMode().getStatusLevel()); + String text = status.getStatusText(); + if ("".equals(text)) { + text = activity.getString(R.string.empty_status); + } + ((TextView) view.findViewById(R.id.name)).setText(text); + return view; + } - @Override - public void onChange() { - statuses.clear(); - statuses.addAll(AccountManager.getInstance().getSavedStatuses()); - Collections.sort(statuses); - notifyDataSetChanged(); - } + @Override + public void onChange() { + statuses.clear(); + statuses.addAll(AccountManager.getInstance().getSavedStatuses()); + Collections.sort(statuses); + notifyDataSetChanged(); + } } diff --git a/app/src/main/java/com/xabber/android/ui/adapter/StatusModeAdapter.java b/app/src/main/java/com/xabber/android/ui/adapter/StatusModeAdapter.java index e4f677ed52..d3c951afbe 100644 --- a/app/src/main/java/com/xabber/android/ui/adapter/StatusModeAdapter.java +++ b/app/src/main/java/com/xabber/android/ui/adapter/StatusModeAdapter.java @@ -1,21 +1,19 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.ui.adapter; -import java.util.ArrayList; - import android.app.Activity; import android.view.View; import android.view.ViewGroup; @@ -23,78 +21,67 @@ import android.widget.ImageView; import android.widget.TextView; +import com.xabber.android.R; import com.xabber.android.data.account.StatusMode; -import com.xabber.androiddev.R; + +import java.util.ArrayList; /** * Adapter for available status modes. - * + * * @author alexander.ivanov - * */ public class StatusModeAdapter extends BaseAdapter { - private final Activity activity; - private final ArrayList statusModes; + private final Activity activity; + private final ArrayList statusModes; - public StatusModeAdapter(Activity activity) { - super(); - this.activity = activity; - statusModes = new ArrayList(); - statusModes.add(StatusMode.chat); - statusModes.add(StatusMode.available); - statusModes.add(StatusMode.away); - statusModes.add(StatusMode.xa); - statusModes.add(StatusMode.dnd); - // statusModes.add(StatusMode.invisible); - statusModes.add(StatusMode.unavailable); - } + public StatusModeAdapter(Activity activity) { + super(); + this.activity = activity; + statusModes = new ArrayList<>(); + statusModes.add(StatusMode.chat); + statusModes.add(StatusMode.available); + statusModes.add(StatusMode.away); + statusModes.add(StatusMode.xa); + statusModes.add(StatusMode.dnd); + statusModes.add(StatusMode.unavailable); + } - @Override - public int getCount() { - return statusModes.size(); - } + @Override + public int getCount() { + return statusModes.size(); + } - @Override - public Object getItem(int position) { - return statusModes.get(position); - } + @Override + public Object getItem(int position) { + return statusModes.get(position); + } - @Override - public long getItemId(int position) { - return position; - } + @Override + public long getItemId(int position) { + return position; + } - private void updateView(int position, View view) { - StatusMode statusMode = (StatusMode) getItem(position); - ((ImageView) view.findViewById(R.id.icon)).setImageLevel(statusMode - .getStatusLevel()); - ((TextView) view.findViewById(R.id.name)).setText(statusMode - .getStringID()); - } + private void updateView(int position, View view) { + StatusMode statusMode = (StatusMode) getItem(position); + ((ImageView) view.findViewById(R.id.icon)).setImageLevel(statusMode.getStatusLevel()); + ((TextView) view.findViewById(R.id.name)).setText(statusMode.getStringID()); + } - @Override - public View getView(int position, View convertView, ViewGroup parent) { - View view; - if (convertView == null) { - view = activity.getLayoutInflater().inflate( - R.layout.status_mode_item, parent, false); - } else { - view = convertView; - } - updateView(position, view); - return view; - } + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View view; + if (convertView == null) { + view = activity.getLayoutInflater().inflate(R.layout.status_mode_item, parent, false); + } else { + view = convertView; + } + updateView(position, view); + return view; + } - @Override - public View getDropDownView(int position, View convertView, ViewGroup parent) { - View view; - if (convertView == null) { - view = activity.getLayoutInflater().inflate( - R.layout.status_mode_dropdown, parent, false); - } else { - view = convertView; - } - updateView(position, view); - return view; - } + @Override + public View getDropDownView(int position, View convertView, ViewGroup parent) { + return getView(position, convertView, parent); + } } diff --git a/app/src/main/java/com/xabber/android/ui/adapter/UpdatableAdapter.java b/app/src/main/java/com/xabber/android/ui/adapter/UpdatableAdapter.java index 609ae86861..40d7a1a7c8 100644 --- a/app/src/main/java/com/xabber/android/ui/adapter/UpdatableAdapter.java +++ b/app/src/main/java/com/xabber/android/ui/adapter/UpdatableAdapter.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -21,13 +21,13 @@ */ public interface UpdatableAdapter { - /** - * Source data was changed. - * - * This function MUST be called from UI thread. - * - * This function must call {@link BaseAdapter#notifyDataSetChanged()} if - * data was actually changed. - */ - void onChange(); + /** + * Source data was changed. + *

+ * This function MUST be called from UI thread. + *

+ * This function must call {@link BaseAdapter#notifyDataSetChanged()} if + * data was actually changed. + */ + void onChange(); } diff --git a/app/src/main/java/com/xabber/android/ui/dialog/AbstractDialogFragment.java b/app/src/main/java/com/xabber/android/ui/dialog/AbstractDialogFragment.java new file mode 100644 index 0000000000..a0f9590534 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/dialog/AbstractDialogFragment.java @@ -0,0 +1,83 @@ +/** + * Copyright (c) 2013, Redsolution LTD. All rights reserved. + * + * This file is part of Xabber project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License, Version 3. + * + * Xabber is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License, + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.xabber.android.ui.dialog; + +import android.app.AlertDialog; +import android.app.AlertDialog.Builder; +import android.app.Dialog; +import android.app.DialogFragment; +import android.os.Bundle; + +import java.util.ArrayList; + +/** + * Base dialog fragment. + *

+ * CONVENTION: Subclass should implement newInstance static method. + * Activities or Fragments should use this method to instantiate DialogFragment. + * + * @author alexander.ivanov + */ +public abstract class AbstractDialogFragment extends DialogFragment { + + private Bundle initArguments() { + Bundle bundle = getArguments(); + if (bundle == null) { + bundle = new Bundle(); + setArguments(bundle); + } + return bundle; + } + + protected AbstractDialogFragment putAgrument(String key, String value) { + initArguments().putString(key, value); + return this; + } + + protected AbstractDialogFragment putAgrument(String key, + ArrayList value) { + initArguments().putStringArrayList(key, value); + return this; + } + + protected AbstractDialogFragment putAgrument(String key, boolean value) { + initArguments().putBoolean(key, value); + return this; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + Builder builder = getBuilder(); + return getDialog(builder); + } + + /** + * Constructs {@link AlertDialog.Builder} instance. + * + * @return + */ + protected abstract Builder getBuilder(); + + /** + * Constructs {@link Dialog} instance. + * + * @param builder + * @return + */ + protected Dialog getDialog(Builder builder) { + return builder.create(); + } + +} diff --git a/app/src/main/java/com/xabber/android/ui/dialog/AccountChooseDialogBuilder.java b/app/src/main/java/com/xabber/android/ui/dialog/AccountChooseDialogBuilder.java deleted file mode 100644 index a333294996..0000000000 --- a/app/src/main/java/com/xabber/android/ui/dialog/AccountChooseDialogBuilder.java +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.xabber.android.ui.dialog; - -import java.util.ArrayList; - -import android.app.Activity; -import android.content.DialogInterface; -import android.content.DialogInterface.OnClickListener; -import android.view.View; -import android.view.ViewGroup; - -import com.xabber.android.data.roster.RosterContact; -import com.xabber.android.data.roster.RosterManager; -import com.xabber.android.ui.adapter.AccountChooseAdapter; - -public class AccountChooseDialogBuilder extends ListenableDialogBuilder { - - private final String user; - private String selected; - - public AccountChooseDialogBuilder(Activity activity, int dialogId, - final ConfirmDialogListener listener, String user) { - super(activity, dialogId); - this.user = user; - this.selected = null; - setOnCancelListener(listener); - setOnDeclineListener(listener); - final Adapter adapter = new Adapter(activity); - setSingleChoiceItems(adapter, -1, new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - selected = (String) adapter.getItem(which); - dialog.dismiss(); - listener.onAccept(AccountChooseDialogBuilder.this); - } - }); - } - - /** - * @return null can be returned. - */ - public String getSelected() { - return selected; - } - - private class Adapter extends AccountChooseAdapter { - - public Adapter(Activity activity) { - super(activity); - ArrayList available = new ArrayList(); - for (RosterContact check : RosterManager.getInstance() - .getContacts()) - if (check.isEnabled() && check.getUser().equals(user)) - available.add(check.getAccount()); - if (!available.isEmpty()) { - accounts.clear(); - accounts.addAll(available); - } - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - return getDropDownView(position, convertView, parent); - } - - } - -} diff --git a/app/src/main/java/com/xabber/android/ui/dialog/AccountChooseDialogFragment.java b/app/src/main/java/com/xabber/android/ui/dialog/AccountChooseDialogFragment.java new file mode 100644 index 0000000000..e12ae19ef3 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/dialog/AccountChooseDialogFragment.java @@ -0,0 +1,80 @@ +package com.xabber.android.ui.dialog; + +import android.app.Activity; +import android.app.AlertDialog.Builder; +import android.app.DialogFragment; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.view.View; +import android.view.ViewGroup; + +import com.xabber.android.data.roster.RosterContact; +import com.xabber.android.data.roster.RosterManager; +import com.xabber.android.ui.adapter.AccountChooseAdapter; + +import java.util.ArrayList; + +public class AccountChooseDialogFragment extends AbstractDialogFragment { + + private static final String USER = "USER"; + private static final String TEXT = "TEXT"; + + /** + * @param user + * @param text + * @return + */ + public static DialogFragment newInstance(String user, String text) { + return new AccountChooseDialogFragment().putAgrument(USER, user) + .putAgrument(TEXT, text); + } + + private String user; + private String text; + + @Override + protected Builder getBuilder() { + user = getArguments().getString(USER); + text = getArguments().getString(TEXT); + final Adapter adapter = new Adapter(getActivity()); + Builder builder = new Builder(getActivity()); + builder.setSingleChoiceItems(adapter, -1, new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + String account = (String) adapter.getItem(which); + OnChoosedListener listener = (OnChoosedListener) getActivity(); + listener.onChoose(account, user, text); + } + }); + return builder; + } + + private class Adapter extends AccountChooseAdapter { + + public Adapter(Activity activity) { + super(activity); + ArrayList available = new ArrayList(); + for (RosterContact check : RosterManager.getInstance() + .getContacts()) + if (check.isEnabled() && check.getUser().equals(user)) + available.add(check.getAccount()); + if (!available.isEmpty()) { + accounts.clear(); + accounts.addAll(available); + } + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + return getDropDownView(position, convertView, parent); + } + + } + + public interface OnChoosedListener { + + void onChoose(String account, String user, String text); + + } + +} diff --git a/app/src/main/java/com/xabber/android/ui/dialog/ChatExportDialogFragment.java b/app/src/main/java/com/xabber/android/ui/dialog/ChatExportDialogFragment.java new file mode 100644 index 0000000000..45952f5e9b --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/dialog/ChatExportDialogFragment.java @@ -0,0 +1,123 @@ +/** + * Copyright (c) 2013, Redsolution LTD. All rights reserved. + * + * This file is part of Xabber project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License, Version 3. + * + * Xabber is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License, + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.xabber.android.ui.dialog; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.AlertDialog.Builder; +import android.app.DialogFragment; +import android.content.Intent; +import android.net.Uri; +import android.os.AsyncTask; +import android.view.View; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.Toast; + +import com.xabber.android.R; +import com.xabber.android.data.Application; +import com.xabber.android.data.NetworkException; +import com.xabber.android.data.account.AccountManager; +import com.xabber.android.data.message.MessageManager; +import com.xabber.android.data.roster.RosterManager; + +import java.io.File; + +public class ChatExportDialogFragment extends ConfirmDialogFragment { + + private static final String ACCOUNT = "ACCOUNT"; + private static final String USER = "USER"; + private String account; + private String user; + private EditText nameView; + private CheckBox sendView; + private Activity activity; + + public static DialogFragment newInstance(String account, String user) { + return new ChatExportDialogFragment().putAgrument(ACCOUNT, account).putAgrument(USER, user); + } + + @Override + protected Builder getBuilder() { + activity = getActivity(); + + account = getArguments().getString(ACCOUNT); + user = getArguments().getString(USER); + AlertDialog.Builder builder = new AlertDialog.Builder(activity); + builder.setTitle(R.string.export_chat_title); + View layout = activity.getLayoutInflater().inflate(R.layout.export_chat, null); + nameView = (EditText) layout.findViewById(R.id.name); + sendView = (CheckBox) layout.findViewById(R.id.send); + nameView.setText(getString(R.string.export_chat_mask, + AccountManager.getInstance().getVerboseName(account), + RosterManager.getInstance().getName(account, user))); + builder.setView(layout); + return builder; + } + + @Override + protected boolean onPositiveClick() { + String name = nameView.getText().toString(); + if ("".equals(name)) { + return false; + } + new ChatExportAsyncTask(account, user, name, sendView.isChecked()).execute(); + return true; + } + + private class ChatExportAsyncTask extends AsyncTask { + + private final String account; + private final String user; + private final String name; + private final boolean send; + + public ChatExportAsyncTask(String account, String user, String name, boolean send) { + this.account = account; + this.user = user; + this.name = name; + this.send = send; + } + + @Override + protected File doInBackground(Void... params) { + try { + return MessageManager.getInstance().exportChat(account, user, name); + } catch (NetworkException e) { + Application.getInstance().onError(e); + return null; + } + } + + @Override + public void onPostExecute(File result) { + if (result == null || activity == null) { + return; + } + + // TODO: Use notification bar to notify about success. + if (send) { + Intent intent = new Intent(android.content.Intent.ACTION_SEND); + intent.setType("text/plain"); + Uri uri = Uri.fromFile(result); + intent.putExtra(android.content.Intent.EXTRA_STREAM, uri); + activity.startActivity(Intent.createChooser(intent, activity.getString(R.string.export_chat))); + } else { + Toast.makeText(activity, R.string.export_chat_done, Toast.LENGTH_LONG).show(); + } + } + + } +} diff --git a/app/src/main/java/com/xabber/android/ui/dialog/ConfirmDialogBuilder.java b/app/src/main/java/com/xabber/android/ui/dialog/ConfirmDialogBuilder.java index c269ef45b4..1e7356ed30 100644 --- a/app/src/main/java/com/xabber/android/ui/dialog/ConfirmDialogBuilder.java +++ b/app/src/main/java/com/xabber/android/ui/dialog/ConfirmDialogBuilder.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,28 +18,24 @@ /** * Yes / No dialog builder. - * + * * @author alexander.ivanov - * */ public class ConfirmDialogBuilder extends ListenableDialogBuilder { - /** - * Yes / No dialog builder. - * - * @param activity - * parent activity. - * @param dialogId - * ID of the dialog. - * @param listener - * listener of actions. - */ - public ConfirmDialogBuilder(Activity activity, int dialogId, - ConfirmDialogListener listener) { - super(activity, dialogId); - setOnCancelListener(listener); - setOnDeclineListener(listener); - setOnAcceptListener(listener); - } + /** + * Yes / No dialog builder. + * + * @param activity parent activity. + * @param dialogId ID of the dialog. + * @param listener listener of actions. + */ + public ConfirmDialogBuilder(Activity activity, int dialogId, + ConfirmDialogListener listener) { + super(activity, dialogId); + setOnCancelListener(listener); + setOnDeclineListener(listener); + setOnAcceptListener(listener); + } } diff --git a/app/src/main/java/com/xabber/android/ui/dialog/ConfirmDialogFragment.java b/app/src/main/java/com/xabber/android/ui/dialog/ConfirmDialogFragment.java new file mode 100644 index 0000000000..53622d23d6 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/dialog/ConfirmDialogFragment.java @@ -0,0 +1,165 @@ +/** + * Copyright (c) 2013, Redsolution LTD. All rights reserved. + * + * This file is part of Xabber project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License, Version 3. + * + * Xabber is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License, + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.xabber.android.ui.dialog; + +import android.annotation.TargetApi; +import android.app.AlertDialog; +import android.app.AlertDialog.Builder; +import android.app.Dialog; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.content.DialogInterface.OnShowListener; +import android.os.Build; +import android.view.View; +import android.widget.Button; + +/** + * Base yes / no dialog fragment. + *

+ * Provides callback methods and option to abort dialog dismissing on button + * click (starting from FROYO version). + * + * @author alexander.ivanov + */ +public abstract class ConfirmDialogFragment extends AbstractDialogFragment { + + private final OnClickListener positiveListener = new OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + if (onPositiveClick()) + supportDismiss(dialog); + } + + }; + + private final OnClickListener neutralListener = new OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + if (onNegativeClicked()) + supportDismiss(dialog); + } + + }; + + private final OnClickListener negativeListener = new OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + if (onNegativeClicked()) + supportDismiss(dialog); + } + + }; + + @Override + protected Dialog getDialog(Builder builder) { + if (hasPositiveButton()) + builder.setPositiveButton(getPositiveTextId(), positiveListener); + if (hasNeutralButton()) + builder.setNeutralButton(getNeutralTextId(), neutralListener); + if (hasNegativeButton()) + builder.setNegativeButton(getNegativeTextId(), negativeListener); + AlertDialog dialog = builder.create(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) + setOnShowListener(dialog); + return dialog; + } + + private void supportDismiss(DialogInterface dialog) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) + dialog.dismiss(); + } + + @TargetApi(Build.VERSION_CODES.FROYO) + private void setOnShowListener(final AlertDialog alertDialog) { + alertDialog.setOnShowListener(new OnShowListener() { + + private void setListener(final int whichButton, + final OnClickListener listener) { + Button button = alertDialog.getButton(whichButton); + if (button == null) + return; + button.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + listener.onClick(alertDialog, whichButton); + } + }); + } + + @Override + public void onShow(DialogInterface dialog) { + setListener(AlertDialog.BUTTON_POSITIVE, positiveListener); + setListener(AlertDialog.BUTTON_NEUTRAL, neutralListener); + setListener(AlertDialog.BUTTON_NEGATIVE, negativeListener); + } + + }); + } + + protected boolean hasPositiveButton() { + return true; + } + + protected boolean hasNeutralButton() { + return false; + } + + protected boolean hasNegativeButton() { + return true; + } + + protected int getPositiveTextId() { + return android.R.string.ok; + } + + protected int getNeutralTextId() { + return android.R.string.unknownName; + } + + protected int getNegativeTextId() { + return android.R.string.cancel; + } + + /** + * Processes positive button click. + * + * @return Whether dialog can be dismissed. + */ + protected boolean onPositiveClick() { + return true; + } + + /** + * Processes neutral button click. + * + * @return Whether dialog can be dismissed. + */ + protected boolean onNeutralClicked() { + return true; + } + + /** + * Processes negative button click. + * + * @return Whether dialog can be dismissed. + */ + protected boolean onNegativeClicked() { + return true; + } + +} diff --git a/app/src/main/java/com/xabber/android/ui/dialog/ConfirmDialogListener.java b/app/src/main/java/com/xabber/android/ui/dialog/ConfirmDialogListener.java index 9ad26d308e..550fb801d2 100644 --- a/app/src/main/java/com/xabber/android/ui/dialog/ConfirmDialogListener.java +++ b/app/src/main/java/com/xabber/android/ui/dialog/ConfirmDialogListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,5 +18,5 @@ * Listen for user reaction on dialog build with {@link ConfirmDialogBuilder}. */ public interface ConfirmDialogListener extends OnCancelListener, - OnDeclineListener, OnAcceptListener { + OnDeclineListener, OnAcceptListener { } diff --git a/app/src/main/java/com/xabber/android/ui/dialog/ContactDeleteDialogFragment.java b/app/src/main/java/com/xabber/android/ui/dialog/ContactDeleteDialogFragment.java new file mode 100644 index 0000000000..371e49a32d --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/dialog/ContactDeleteDialogFragment.java @@ -0,0 +1,55 @@ +package com.xabber.android.ui.dialog; + +import android.app.AlertDialog.Builder; +import android.app.DialogFragment; + +import com.xabber.android.R; +import com.xabber.android.data.Application; +import com.xabber.android.data.NetworkException; +import com.xabber.android.data.account.AccountManager; +import com.xabber.android.data.roster.RosterManager; +import com.xabber.android.ui.ContactList; +import com.xabber.android.ui.ContactViewer; + +public class ContactDeleteDialogFragment extends ConfirmDialogFragment { + + private static final String ACCOUNT = "ACCOUNT"; + private static final String USER = "USER"; + private String user; + private String account; + + /** + * @param account + * @param user + * @return + */ + public static DialogFragment newInstance(String account, String user) { + return new ContactDeleteDialogFragment().putAgrument(ACCOUNT, account) + .putAgrument(USER, user); + } + + @Override + protected Builder getBuilder() { + user = getArguments().getString(USER); + account = getArguments().getString(ACCOUNT); + return new Builder(getActivity()).setMessage(getString( + R.string.contact_delete_confirm, RosterManager.getInstance() + .getName(account, user), AccountManager.getInstance() + .getVerboseName(account))); + } + + @Override + protected boolean onPositiveClick() { + try { + RosterManager.getInstance().removeContact(account, user); + } catch (NetworkException e) { + Application.getInstance().onError(e); + } + + if (getActivity() instanceof ContactViewer) { + startActivity(ContactList.createIntent(getActivity())); + } + return true; + } + +} diff --git a/app/src/main/java/com/xabber/android/ui/dialog/ContactIntegrationDialogFragment.java b/app/src/main/java/com/xabber/android/ui/dialog/ContactIntegrationDialogFragment.java new file mode 100644 index 0000000000..e04c6c5433 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/dialog/ContactIntegrationDialogFragment.java @@ -0,0 +1,36 @@ +package com.xabber.android.ui.dialog; + +import android.app.AlertDialog.Builder; +import android.app.DialogFragment; + +import com.xabber.android.R; +import com.xabber.android.data.SettingsManager; +import com.xabber.android.data.account.AccountManager; + +public class ContactIntegrationDialogFragment extends ConfirmDialogFragment { + + public static DialogFragment newInstance() { + return new ContactIntegrationDialogFragment(); + } + + @Override + protected Builder getBuilder() { + return new Builder(getActivity()) + .setMessage(R.string.contact_integration_suggest); + } + + @Override + protected boolean onPositiveClick() { + SettingsManager.setContactIntegrationSuggested(); + for (String account : AccountManager.getInstance().getAllAccounts()) + AccountManager.getInstance().setSyncable(account, true); + return true; + } + + @Override + protected boolean onNegativeClicked() { + SettingsManager.setContactIntegrationSuggested(); + return true; + } + +} diff --git a/app/src/main/java/com/xabber/android/ui/dialog/DialogBuilder.java b/app/src/main/java/com/xabber/android/ui/dialog/DialogBuilder.java index 9b62ca7ca0..b55fef0939 100644 --- a/app/src/main/java/com/xabber/android/ui/dialog/DialogBuilder.java +++ b/app/src/main/java/com/xabber/android/ui/dialog/DialogBuilder.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,47 +20,44 @@ /** * Builder for auto removed dialog on dismiss. - * + * * @author alexander.ivanov - * */ public class DialogBuilder extends AlertDialog.Builder implements - DialogInterface.OnDismissListener { - - protected final Activity activity; - protected final int dialogId; - - /** - * @param activity - * Parent activity. - * @param dialogId - * Dialog ID to be removed. - */ - public DialogBuilder(Activity activity, int dialogId) { - super(activity); - this.activity = activity; - this.dialogId = dialogId; - } - - @Override - public AlertDialog create() { - AlertDialog alertDialog = super.create(); - alertDialog.setOnDismissListener(this); - return alertDialog; - } - - @Override - public void onDismiss(DialogInterface dialog) { - activity.removeDialog(dialogId); - } - - /** - * Returns dialog ID. - * - * @return - */ - public int getDialogId() { - return dialogId; - } + DialogInterface.OnDismissListener { + + protected final Activity activity; + protected final int dialogId; + + /** + * @param activity Parent activity. + * @param dialogId Dialog ID to be removed. + */ + public DialogBuilder(Activity activity, int dialogId) { + super(activity); + this.activity = activity; + this.dialogId = dialogId; + } + + @Override + public AlertDialog create() { + AlertDialog alertDialog = super.create(); + alertDialog.setOnDismissListener(this); + return alertDialog; + } + + @Override + public void onDismiss(DialogInterface dialog) { + activity.removeDialog(dialogId); + } + + /** + * Returns dialog ID. + * + * @return + */ + public int getDialogId() { + return dialogId; + } } diff --git a/app/src/main/java/com/xabber/android/ui/dialog/ExportChatDialogBuilder.java b/app/src/main/java/com/xabber/android/ui/dialog/ExportChatDialogBuilder.java deleted file mode 100644 index 0fbcf087fa..0000000000 --- a/app/src/main/java/com/xabber/android/ui/dialog/ExportChatDialogBuilder.java +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.xabber.android.ui.dialog; - -import android.app.Activity; -import android.content.DialogInterface; -import android.view.View; -import android.widget.CheckBox; -import android.widget.EditText; -import android.widget.Toast; - -import com.xabber.android.data.account.AccountManager; -import com.xabber.android.data.roster.RosterManager; -import com.xabber.androiddev.R; - -public class ExportChatDialogBuilder extends ConfirmDialogBuilder { - - private final EditText nameView; - private final CheckBox sendView; - - public ExportChatDialogBuilder(Activity activity, int dialogId, - ConfirmDialogListener listener, String account, String user) { - super(activity, dialogId, listener); - setTitle(R.string.export_chat_title); - View layout = activity.getLayoutInflater().inflate( - R.layout.export_chat, null); - nameView = (EditText) layout.findViewById(R.id.name); - sendView = (CheckBox) layout.findViewById(R.id.send); - nameView.setText(activity.getString(R.string.export_chat_mask, - AccountManager.getInstance().getVerboseName(account), - RosterManager.getInstance().getName(account, user))); - setView(layout); - } - - @Override - public void onAccept(DialogInterface dialog) { - if ("".equals(getName())) { - Toast.makeText(activity, - activity.getString(R.string.group_is_empty), - Toast.LENGTH_LONG).show(); - return; - } - super.onAccept(dialog); - } - - public String getName() { - return nameView.getText().toString(); - } - - public boolean isSendChecked() { - return sendView.isChecked(); - } -} diff --git a/app/src/main/java/com/xabber/android/ui/dialog/GroupAddDialogBuilder.java b/app/src/main/java/com/xabber/android/ui/dialog/GroupAddDialogBuilder.java deleted file mode 100644 index 99c3658300..0000000000 --- a/app/src/main/java/com/xabber/android/ui/dialog/GroupAddDialogBuilder.java +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.xabber.android.ui.dialog; - -import java.util.Collection; - -import android.app.Activity; -import android.content.DialogInterface; -import android.view.View; -import android.widget.EditText; -import android.widget.Toast; - -import com.xabber.androiddev.R; - -public class GroupAddDialogBuilder extends ConfirmDialogBuilder { - - private final Collection groups; - private final EditText nameView; - - public GroupAddDialogBuilder(Activity activity, int dialogId, - ConfirmDialogListener listener, Collection groups) { - super(activity, dialogId, listener); - setTitle(R.string.group_add); - this.groups = groups; - View layout = activity.getLayoutInflater().inflate(R.layout.group_name, - null); - nameView = (EditText) layout.findViewById(R.id.group_name); - setView(layout); - } - - @Override - public void onAccept(DialogInterface dialog) { - String name = nameView.getText().toString(); - if ("".equals(name)) { - Toast.makeText(activity, - activity.getString(R.string.group_is_empty), - Toast.LENGTH_LONG).show(); - return; - } - if (groups.contains(name)) { - Toast.makeText(activity, activity.getString(R.string.group_exists), - Toast.LENGTH_LONG).show(); - return; - } - super.onAccept(dialog); - } - - public String getName() { - return nameView.getText().toString(); - } - -} diff --git a/app/src/main/java/com/xabber/android/ui/dialog/GroupDeleteDialogFragment.java b/app/src/main/java/com/xabber/android/ui/dialog/GroupDeleteDialogFragment.java new file mode 100644 index 0000000000..623d43ad90 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/dialog/GroupDeleteDialogFragment.java @@ -0,0 +1,49 @@ +package com.xabber.android.ui.dialog; + +import android.app.AlertDialog.Builder; +import android.app.DialogFragment; + +import com.xabber.android.R; +import com.xabber.android.data.Application; +import com.xabber.android.data.NetworkException; +import com.xabber.android.data.roster.RosterManager; + +public class GroupDeleteDialogFragment extends ConfirmDialogFragment { + + private static final String ACCOUNT = "ACCOUNT"; + private static final String GROUP = "GROUP"; + private String group; + private String account; + + /** + * @param account can be null to be used for all accounts. + * @param group + * @return + */ + public static DialogFragment newInstance(String account, String group) { + return new GroupDeleteDialogFragment().putAgrument(ACCOUNT, account) + .putAgrument(GROUP, group); + } + + @Override + protected Builder getBuilder() { + group = getArguments().getString(GROUP); + account = getArguments().getString(ACCOUNT); + return new Builder(getActivity()).setMessage(getString( + R.string.group_remove_confirm, group)); + } + + @Override + protected boolean onPositiveClick() { + try { + if (account == null) + RosterManager.getInstance().removeGroup(group); + else + RosterManager.getInstance().removeGroup(account, group); + } catch (NetworkException e) { + Application.getInstance().onError(e); + } + return true; + } + +} diff --git a/app/src/main/java/com/xabber/android/ui/dialog/GroupRenameDialogBuilder.java b/app/src/main/java/com/xabber/android/ui/dialog/GroupRenameDialogBuilder.java deleted file mode 100644 index 07e9bc1edd..0000000000 --- a/app/src/main/java/com/xabber/android/ui/dialog/GroupRenameDialogBuilder.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.xabber.android.ui.dialog; - -import android.app.Activity; -import android.content.DialogInterface; -import android.view.View; -import android.widget.EditText; -import android.widget.Toast; - -import com.xabber.androiddev.R; - -public class GroupRenameDialogBuilder extends ConfirmDialogBuilder { - - private final EditText nameView; - - public GroupRenameDialogBuilder(Activity activity, int dialogId, - ConfirmDialogListener listener, String group) { - super(activity, dialogId, listener); - setTitle(R.string.group_rename); - View layout = activity.getLayoutInflater().inflate(R.layout.group_name, - null); - nameView = (EditText) layout.findViewById(R.id.group_name); - nameView.setText(group); - setView(layout); - } - - @Override - public void onAccept(DialogInterface dialog) { - if ("".equals(getName())) { - Toast.makeText(activity, - activity.getString(R.string.group_is_empty), - Toast.LENGTH_LONG).show(); - return; - } - super.onAccept(dialog); - } - - public String getName() { - return nameView.getText().toString(); - } - -} diff --git a/app/src/main/java/com/xabber/android/ui/dialog/GroupRenameDialogFragment.java b/app/src/main/java/com/xabber/android/ui/dialog/GroupRenameDialogFragment.java new file mode 100644 index 0000000000..0cbdb1bc6d --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/dialog/GroupRenameDialogFragment.java @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2013, Redsolution LTD. All rights reserved. + * + * This file is part of Xabber project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License, Version 3. + * + * Xabber is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License, + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.xabber.android.ui.dialog; + +import android.app.AlertDialog; +import android.app.AlertDialog.Builder; +import android.app.DialogFragment; +import android.view.View; +import android.widget.EditText; +import android.widget.Toast; + +import com.xabber.android.R; +import com.xabber.android.data.Application; +import com.xabber.android.data.NetworkException; +import com.xabber.android.data.roster.RosterManager; + +public class GroupRenameDialogFragment extends ConfirmDialogFragment { + + private static final String ACCOUNT = "ACCOUNT"; + private static final String GROUP = "GROUP"; + private String group; + private String account; + private EditText nameView; + + /** + * @param account can be null to be used for all accounts. + * @param group can be null to be used for "no group". + * @return + */ + public static DialogFragment newInstance(String account, String group) { + return new GroupRenameDialogFragment().putAgrument(ACCOUNT, account) + .putAgrument(GROUP, group); + } + + @Override + protected Builder getBuilder() { + group = getArguments().getString(GROUP); + account = getArguments().getString(ACCOUNT); + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setTitle(R.string.group_rename); + View layout = getActivity().getLayoutInflater().inflate( + R.layout.group_name, null); + nameView = (EditText) layout.findViewById(R.id.group_name); + nameView.setText(group == null ? "" : group); + builder.setView(layout); + return builder; + } + + @Override + protected boolean onPositiveClick() { + String newName = nameView.getText().toString(); + if ("".equals(newName)) { + Toast.makeText(getActivity(), getString(R.string.group_is_empty), + Toast.LENGTH_LONG).show(); + return false; + } + try { + if (account == null) + RosterManager.getInstance().renameGroup(group, newName); + else + RosterManager.getInstance() + .renameGroup(account, group, newName); + } catch (NetworkException e) { + Application.getInstance().onError(e); + } + return true; + } + +} diff --git a/app/src/main/java/com/xabber/android/ui/dialog/ListenableDialogBuilder.java b/app/src/main/java/com/xabber/android/ui/dialog/ListenableDialogBuilder.java index 6fbb1775d4..656a1b1ac1 100644 --- a/app/src/main/java/com/xabber/android/ui/dialog/ListenableDialogBuilder.java +++ b/app/src/main/java/com/xabber/android/ui/dialog/ListenableDialogBuilder.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -21,99 +21,98 @@ /** * Dialog builder that provides listeners for dialog to be accepted, declined * and canceled. - * + * * @author alexander.ivanov - * */ public class ListenableDialogBuilder extends DialogBuilder { - private OnCancelListener onCancelListener; - private OnDeclineListener onDeclineListener; - private OnAcceptListener onAcceptListener; + private OnCancelListener onCancelListener; + private OnDeclineListener onDeclineListener; + private OnAcceptListener onAcceptListener; - public ListenableDialogBuilder(Activity activity, int dialogId) { - super(activity, dialogId); - } + public ListenableDialogBuilder(Activity activity, int dialogId) { + super(activity, dialogId); + } - /** - * Sets listener for dialog to be canceled. - * - * @param listener - * @return - */ - public ListenableDialogBuilder setOnCancelListener(OnCancelListener listener) { - onCancelListener = listener; - setOnCancelListener(new DialogInterface.OnCancelListener() { - @Override - public void onCancel(DialogInterface dialog) { - ListenableDialogBuilder.this.onCancel(dialog); - } - }); - return this; - } + /** + * Sets listener for dialog to be canceled. + * + * @param listener + * @return + */ + public ListenableDialogBuilder setOnCancelListener(OnCancelListener listener) { + onCancelListener = listener; + setOnCancelListener(new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + ListenableDialogBuilder.this.onCancel(dialog); + } + }); + return this; + } - protected void onCancel(DialogInterface dialog) { - dialog.dismiss(); - onCancelListener.onCancel(this); - } + protected void onCancel(DialogInterface dialog) { + dialog.dismiss(); + onCancelListener.onCancel(this); + } - /** - * @return Title for the negative button. - */ - protected String getNegativeTitle() { - return activity.getString(android.R.string.no); - } + /** + * @return Title for the negative button. + */ + protected String getNegativeTitle() { + return activity.getString(android.R.string.no); + } - /** - * Sets listener for dialog to be declined by pushing on negative button. - * - * @param listener - * @return - */ - public ListenableDialogBuilder setOnDeclineListener( - OnDeclineListener listener) { - onDeclineListener = listener; - setNegativeButton(getNegativeTitle(), new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - ListenableDialogBuilder.this.onDecline(dialog); - } - }); - return this; - } + /** + * Sets listener for dialog to be declined by pushing on negative button. + * + * @param listener + * @return + */ + public ListenableDialogBuilder setOnDeclineListener( + OnDeclineListener listener) { + onDeclineListener = listener; + setNegativeButton(getNegativeTitle(), new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + ListenableDialogBuilder.this.onDecline(dialog); + } + }); + return this; + } - protected void onDecline(DialogInterface dialog) { - dialog.dismiss(); - onDeclineListener.onDecline(this); - } + protected void onDecline(DialogInterface dialog) { + dialog.dismiss(); + onDeclineListener.onDecline(this); + } - /** - * @return Title for the positive button. - */ - protected String getPositiveTitle() { - return activity.getString(android.R.string.yes); - } + /** + * @return Title for the positive button. + */ + protected String getPositiveTitle() { + return activity.getString(android.R.string.yes); + } - /** - * Sets listener for dialog to be accepted by pushing on positive button. - * - * @param listener - * @return - */ - public ListenableDialogBuilder setOnAcceptListener(OnAcceptListener listener) { - onAcceptListener = listener; - setPositiveButton(getPositiveTitle(), new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - ListenableDialogBuilder.this.onAccept(dialog); - } - }); - return this; - } + /** + * Sets listener for dialog to be accepted by pushing on positive button. + * + * @param listener + * @return + */ + public ListenableDialogBuilder setOnAcceptListener(OnAcceptListener listener) { + onAcceptListener = listener; + setPositiveButton(getPositiveTitle(), new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + ListenableDialogBuilder.this.onAccept(dialog); + } + }); + return this; + } - public void onAccept(DialogInterface dialog) { - dialog.dismiss(); - onAcceptListener.onAccept(this); - } + public void onAccept(DialogInterface dialog) { + dialog.dismiss(); + onAcceptListener.onAccept(this); + } } diff --git a/app/src/main/java/com/xabber/android/ui/dialog/MUCDeleteDialogFragment.java b/app/src/main/java/com/xabber/android/ui/dialog/MUCDeleteDialogFragment.java new file mode 100644 index 0000000000..b7a0f10230 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/dialog/MUCDeleteDialogFragment.java @@ -0,0 +1,49 @@ +package com.xabber.android.ui.dialog; + +import android.app.AlertDialog.Builder; +import android.app.DialogFragment; + +import com.xabber.android.R; +import com.xabber.android.data.account.AccountManager; +import com.xabber.android.data.extension.muc.MUCManager; +import com.xabber.android.data.message.MessageManager; +import com.xabber.android.data.notification.NotificationManager; +import com.xabber.android.data.roster.RosterManager; + +public class MUCDeleteDialogFragment extends ConfirmDialogFragment { + + private static final String ACCOUNT = "ACCOUNT"; + private static final String USER = "USER"; + private String user; + private String account; + + /** + * @param account + * @param user + * @return + */ + public static DialogFragment newInstance(String account, String user) { + return new MUCDeleteDialogFragment().putAgrument(ACCOUNT, account) + .putAgrument(USER, user); + } + + @Override + protected Builder getBuilder() { + user = getArguments().getString(USER); + account = getArguments().getString(ACCOUNT); + return new Builder(getActivity()).setMessage(getString( + R.string.muc_delete_confirm, RosterManager.getInstance() + .getName(account, user), AccountManager.getInstance() + .getVerboseName(account))); + } + + @Override + protected boolean onPositiveClick() { + MUCManager.getInstance().removeRoom(account, user); + MessageManager.getInstance().closeChat(account, user); + NotificationManager.getInstance().removeMessageNotification(account, + user); + return true; + } + +} diff --git a/app/src/main/java/com/xabber/android/ui/dialog/NotificationDialogBuilder.java b/app/src/main/java/com/xabber/android/ui/dialog/NotificationDialogBuilder.java index ba2276a40a..8b45834454 100644 --- a/app/src/main/java/com/xabber/android/ui/dialog/NotificationDialogBuilder.java +++ b/app/src/main/java/com/xabber/android/ui/dialog/NotificationDialogBuilder.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,22 +18,21 @@ /** * Builder of one button dialog. - * + * * @author alexander.ivanov - * */ public class NotificationDialogBuilder extends ListenableDialogBuilder { - public NotificationDialogBuilder(Activity activity, int dialogId, - NotificationDialogListener listener) { - super(activity, dialogId); - setOnCancelListener(listener); - setOnAcceptListener(listener); - } + public NotificationDialogBuilder(Activity activity, int dialogId, + NotificationDialogListener listener) { + super(activity, dialogId); + setOnCancelListener(listener); + setOnAcceptListener(listener); + } - @Override - protected String getPositiveTitle() { - return activity.getString(android.R.string.ok); - } + @Override + protected String getPositiveTitle() { + return activity.getString(android.R.string.ok); + } } diff --git a/app/src/main/java/com/xabber/android/ui/dialog/NotificationDialogListener.java b/app/src/main/java/com/xabber/android/ui/dialog/NotificationDialogListener.java index 4f830824f5..d46e96420b 100644 --- a/app/src/main/java/com/xabber/android/ui/dialog/NotificationDialogListener.java +++ b/app/src/main/java/com/xabber/android/ui/dialog/NotificationDialogListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -19,5 +19,5 @@ * {@link NotificationDialogBuilder}. */ public interface NotificationDialogListener extends OnCancelListener, - OnAcceptListener { + OnAcceptListener { } diff --git a/app/src/main/java/com/xabber/android/ui/dialog/OnAcceptListener.java b/app/src/main/java/com/xabber/android/ui/dialog/OnAcceptListener.java index 239244cbac..4f5cd0ac3c 100644 --- a/app/src/main/java/com/xabber/android/ui/dialog/OnAcceptListener.java +++ b/app/src/main/java/com/xabber/android/ui/dialog/OnAcceptListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -19,9 +19,9 @@ */ public interface OnAcceptListener { - /** - * Request was accepted. - */ - void onAccept(DialogBuilder dialogBuilder); + /** + * Request was accepted. + */ + void onAccept(DialogBuilder dialogBuilder); } diff --git a/app/src/main/java/com/xabber/android/ui/dialog/OnCancelListener.java b/app/src/main/java/com/xabber/android/ui/dialog/OnCancelListener.java index 836b6343d6..db34895e32 100644 --- a/app/src/main/java/com/xabber/android/ui/dialog/OnCancelListener.java +++ b/app/src/main/java/com/xabber/android/ui/dialog/OnCancelListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -19,9 +19,9 @@ */ public interface OnCancelListener { - /** - * Dialog was canceled. - */ - void onCancel(DialogBuilder dialogBuilder); + /** + * Dialog was canceled. + */ + void onCancel(DialogBuilder dialogBuilder); } diff --git a/app/src/main/java/com/xabber/android/ui/dialog/OnDeclineListener.java b/app/src/main/java/com/xabber/android/ui/dialog/OnDeclineListener.java index b7a0ac7993..5f856c8b75 100644 --- a/app/src/main/java/com/xabber/android/ui/dialog/OnDeclineListener.java +++ b/app/src/main/java/com/xabber/android/ui/dialog/OnDeclineListener.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -19,9 +19,9 @@ */ public interface OnDeclineListener { - /** - * Request was declined. - */ - void onDecline(DialogBuilder dialogBuilder); + /** + * Request was declined. + */ + void onDecline(DialogBuilder dialogBuilder); } diff --git a/app/src/main/java/com/xabber/android/ui/dialog/OrbotInstallerDialogBuilder.java b/app/src/main/java/com/xabber/android/ui/dialog/OrbotInstallerDialogBuilder.java index 46114b76a8..8486b6646a 100644 --- a/app/src/main/java/com/xabber/android/ui/dialog/OrbotInstallerDialogBuilder.java +++ b/app/src/main/java/com/xabber/android/ui/dialog/OrbotInstallerDialogBuilder.java @@ -1,53 +1,52 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.ui.dialog; import android.app.Activity; +import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; +import com.xabber.android.R; import com.xabber.android.ui.helper.OrbotHelper; -import com.xabber.androiddev.R; /** * Orbot installer dialog builder. - * + * * @author alexander.ivanov - * */ -public class OrbotInstallerDialogBuilder extends DialogBuilder { +public class OrbotInstallerDialogBuilder { - private final static String MARKET_SEARCH = "market://search?q=pname:%s"; + private final static String MARKET_SEARCH = "market://search?q=pname:%s"; - public OrbotInstallerDialogBuilder(final Activity activity, int dialogId) { - super(activity, dialogId); - setIcon(android.R.drawable.ic_dialog_alert); - setTitle(R.string.orbot_required_title); - setMessage(R.string.orbot_required_message); - setPositiveButton(android.R.string.yes, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int w) { - Uri uri = Uri.parse(String.format(MARKET_SEARCH, - OrbotHelper.URI_ORBOT)); - Intent intent = new Intent(Intent.ACTION_VIEW, uri); - activity.startActivity(intent); - } - }); - setNegativeButton(android.R.string.no, null); - } + public static void show(final Activity activity) { + new AlertDialog.Builder(activity) + .setTitle(R.string.orbot_required_title) + .setMessage(R.string.orbot_required_message) + .setPositiveButton(android.R.string.yes, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int w) { + Uri uri = Uri.parse(String.format(MARKET_SEARCH, OrbotHelper.URI_ORBOT)); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + activity.startActivity(intent); + } + }) + .setNegativeButton(android.R.string.no, null) + .show(); + } } diff --git a/app/src/main/java/com/xabber/android/ui/dialog/StartAtBootDialogFragment.java b/app/src/main/java/com/xabber/android/ui/dialog/StartAtBootDialogFragment.java new file mode 100644 index 0000000000..2076265c53 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/dialog/StartAtBootDialogFragment.java @@ -0,0 +1,34 @@ +package com.xabber.android.ui.dialog; + +import android.app.AlertDialog.Builder; +import android.app.DialogFragment; + +import com.xabber.android.R; +import com.xabber.android.data.SettingsManager; + +public class StartAtBootDialogFragment extends ConfirmDialogFragment { + + public static DialogFragment newInstance() { + return new StartAtBootDialogFragment(); + } + + @Override + protected Builder getBuilder() { + return new Builder(getActivity()) + .setMessage(R.string.start_at_boot_suggest); + } + + @Override + protected boolean onPositiveClick() { + SettingsManager.setStartAtBootSuggested(); + SettingsManager.setConnectionStartAtBoot(true); + return true; + } + + @Override + protected boolean onNegativeClicked() { + SettingsManager.setStartAtBootSuggested(); + return true; + } + +} diff --git a/app/src/main/java/com/xabber/android/ui/dialog/TranslationDialog.java b/app/src/main/java/com/xabber/android/ui/dialog/TranslationDialog.java new file mode 100644 index 0000000000..3f820028ba --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/dialog/TranslationDialog.java @@ -0,0 +1,35 @@ +package com.xabber.android.ui.dialog; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.content.DialogInterface; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; + +import com.xabber.android.R; + +public class TranslationDialog extends DialogFragment implements DialogInterface.OnClickListener { + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + return new AlertDialog.Builder(getActivity()) + .setTitle(R.string.translation_unavailable) + .setMessage(R.string.translation_unavailable_message) + .setNegativeButton(R.string.help_translate_xabber, this) + .setPositiveButton(android.R.string.no, this) + .setCancelable(false) + .create(); + } + + @Override + public void onClick(DialogInterface dialog, int which) { + switch (which) { + case Dialog.BUTTON_NEGATIVE: + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse(getString(R.string.translation_url))); + startActivity(i); + break; + } + } +} diff --git a/app/src/main/java/com/xabber/android/ui/helper/AbstractAvatarInflaterHelper.java b/app/src/main/java/com/xabber/android/ui/helper/AbstractAvatarInflaterHelper.java deleted file mode 100644 index 87b8227e2d..0000000000 --- a/app/src/main/java/com/xabber/android/ui/helper/AbstractAvatarInflaterHelper.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.xabber.android.ui.helper; - -import android.widget.ImageView; - -import com.xabber.android.data.Application; -import com.xabber.android.data.roster.AbstractContact; - -/** - * Helper class to update avatar's contact item. - * - * @author alexander.ivanov - * - */ -public abstract class AbstractAvatarInflaterHelper { - - /** - * Update avatar image view. - * - * @param avatar - * @param abstractContact - */ - public abstract void updateAvatar(ImageView avatar, - AbstractContact abstractContact); - - /** - * @return New instance depend on whether new system contact list is - * supported. - */ - public static AbstractAvatarInflaterHelper createAbstractContactInflaterHelper() { - if (Application.getInstance().isContactsSupported()) - return new AvatarInflaterHelper(); - else - return new DummyAvatarInflaterHelper(); - } - -} diff --git a/app/src/main/java/com/xabber/android/ui/helper/AccountPainter.java b/app/src/main/java/com/xabber/android/ui/helper/AccountPainter.java new file mode 100644 index 0000000000..e636710a0e --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/helper/AccountPainter.java @@ -0,0 +1,120 @@ +package com.xabber.android.ui.helper; + +import android.content.Context; +import android.content.res.TypedArray; + +import com.xabber.android.R; +import com.xabber.android.data.account.AccountManager; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class AccountPainter { + private final int themeMainColor; + private final int themeDarkColor; + private final String[] accountColorNames; + + private int[] accountMainColors; + private int[] accountDarkColors; + private int[] accountDarkestColors; + + public AccountPainter(Context context) { + + accountMainColors = context.getResources().getIntArray(R.array.account_action_bar); + accountDarkColors = context.getResources().getIntArray(R.array.account_status_bar); + accountDarkestColors = context.getResources().getIntArray(R.array.account_900); + + accountColorNames = context.getResources().getStringArray(R.array.account_color_names); + + themeMainColor = getThemeMainColor(context); + themeDarkColor = getThemeDarkColor(context); + } + + private static String getFirstAccount() { + List list = new ArrayList<>(); + list.addAll(AccountManager.getInstance().getAccounts()); + Collections.sort(list); + if (list.isEmpty()) { + return null; + } else { + return list.get(0); + } + } + + private static int getAccountColorLevel(String account) { + return AccountManager.getInstance().getColorLevel(account); + } + + public static int getDefaultAccountColorLevel() { + String firstAccount = getFirstAccount(); + if (firstAccount == null) { + return 5; + } else { + return getAccountColorLevel(firstAccount); + } + } + + private int getThemeMainColor(Context context) { + TypedArray a = context.getTheme().obtainStyledAttributes(R.style.Theme, new int[]{R.attr.colorPrimary}); + int attributeResourceId = a.getResourceId(0, 0); + a.recycle(); + return context.getResources().getColor(attributeResourceId); + } + + private int getThemeDarkColor(Context context) { + TypedArray a = context.getTheme().obtainStyledAttributes(R.style.Theme, new int[]{R.attr.colorPrimaryDark}); + int attributeResourceId = a.getResourceId(0, 0); + a.recycle(); + return context.getResources().getColor(attributeResourceId); + } + + public int getAccountMainColor(String account) { + return accountMainColors[getAccountColorLevel(account)]; + } + + public int getDefaultMainColor() { + String firstAccount = getFirstAccount(); + if (firstAccount == null) { + return themeMainColor; + } else { + return getAccountMainColor(firstAccount); + } + } + + public int getAccountDarkColor(String account) { + return accountDarkColors[getAccountColorLevel(account)]; + } + + public int getAccountDarkestColor(String account) { + return accountDarkestColors[getAccountColorLevel(account)]; + } + + + public int getDefaultDarkColor() { + String firstAccount = getFirstAccount(); + if (firstAccount == null) { + return themeDarkColor; + } else { + return getAccountDarkColor(firstAccount); + } + } + + public int getAccountMainColorByColorName(String targetColorName) { + return accountMainColors[getColorIndexByName(targetColorName)]; + } + + public int getAccountDarkColorByColorName(String targetColorName) { + return accountDarkColors[getColorIndexByName(targetColorName)]; + } + + private Integer getColorIndexByName(String targetColorName) { + for (int i = 0; i < accountColorNames.length; i++) { + String accountColorName = accountColorNames[i]; + if (accountColorName.equals(targetColorName)) { + return i; + } + } + return null; + } +} diff --git a/app/src/main/java/com/xabber/android/ui/helper/AvatarInflaterHelper.java b/app/src/main/java/com/xabber/android/ui/helper/AvatarInflaterHelper.java deleted file mode 100644 index 3453d94dd6..0000000000 --- a/app/src/main/java/com/xabber/android/ui/helper/AvatarInflaterHelper.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.xabber.android.ui.helper; - -import android.annotation.TargetApi; -import android.provider.ContactsContract; -import android.widget.ImageView; -import android.widget.QuickContactBadge; - -import com.xabber.android.data.roster.AbstractContact; - -/** - * Helper class to add quick contact badge to the inflated contact item. - * - * @author alexander.ivanov - * - */ -@TargetApi(5) -public class AvatarInflaterHelper extends AbstractAvatarInflaterHelper { - - @Override - public void updateAvatar(ImageView avatar, AbstractContact abstractContact) { - QuickContactBadge badge = (QuickContactBadge) avatar; - badge.assignContactFromEmail(abstractContact.getUser(), true); - badge.setMode(ContactsContract.QuickContact.MODE_SMALL); - } - -} diff --git a/app/src/main/java/com/xabber/android/ui/helper/BarPainter.java b/app/src/main/java/com/xabber/android/ui/helper/BarPainter.java new file mode 100644 index 0000000000..b5229526af --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/helper/BarPainter.java @@ -0,0 +1,34 @@ +package com.xabber.android.ui.helper; + +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; + +public class BarPainter { + + private final Toolbar toolbar; + private StatusBarPainter statusBarPainter; + + private AccountPainter accountPainter; + + public BarPainter(AppCompatActivity activity, Toolbar toolbar) { + this.toolbar = toolbar; + statusBarPainter = new StatusBarPainter(activity); + + accountPainter = new AccountPainter(activity); + } + + public void updateWithAccountName(String account) { + toolbar.setBackgroundColor(accountPainter.getAccountMainColor(account)); + statusBarPainter.updateWithAccountName(account); + } + + public void setDefaultColor() { + toolbar.setBackgroundColor(accountPainter.getDefaultMainColor()); + statusBarPainter.updateWithDefaultColor(); + } + + public void updateWithColorName(String targetColorName) { + toolbar.setBackgroundColor(accountPainter.getAccountMainColorByColorName(targetColorName)); + statusBarPainter.updateWithColor(accountPainter.getAccountDarkColorByColorName(targetColorName)); + } +} diff --git a/app/src/main/java/com/xabber/android/ui/helper/BaseListEditor.java b/app/src/main/java/com/xabber/android/ui/helper/BaseListEditor.java deleted file mode 100644 index 3a1b6665ff..0000000000 --- a/app/src/main/java/com/xabber/android/ui/helper/BaseListEditor.java +++ /dev/null @@ -1,204 +0,0 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.xabber.android.ui.helper; - -import android.app.Dialog; -import android.content.Intent; -import android.os.Bundle; -import android.view.ContextMenu; -import android.view.ContextMenu.ContextMenuInfo; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.widget.AdapterView; -import android.widget.AdapterView.AdapterContextMenuInfo; -import android.widget.ListView; -import android.widget.TextView; - -import com.xabber.android.ui.adapter.BaseListEditorAdapter; -import com.xabber.android.ui.dialog.ConfirmDialogBuilder; -import com.xabber.android.ui.dialog.ConfirmDialogListener; -import com.xabber.android.ui.dialog.DialogBuilder; -import com.xabber.androiddev.R; - -/** - * Provide possibility to add, edit and delete list items. - * - * @author alexander.ivanov - * - * @param - */ -public abstract class BaseListEditor extends ManagedListActivity implements - AdapterView.OnItemClickListener, ConfirmDialogListener { - - private static final String SAVED_ACTION_WITH = "com.xabber.android.ui.BaseListActivity.SAVED_ACTION_WITH"; - - private static final int OPTION_MENU_ADD_ID = Menu.FIRST; - private static final int CONTEXT_MENU_DELETE_ID = 0x10; - private static final int DIALOG_DELETE_ID = 0x100; - - private T actionWith; - - private BaseListEditorAdapter adapter; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (isFinishing()) - return; - onInflate(savedInstanceState); - if (savedInstanceState != null) - actionWith = getSavedValue(savedInstanceState, SAVED_ACTION_WITH); - else - actionWith = null; - ListView listView = getListView(); - LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE); - View view = inflater.inflate(R.layout.add_item, null, true); - ((TextView) view.findViewById(android.R.id.message)) - .setText(getAddTextResourceId()); - listView.addFooterView(view, null, true); - listView.setOnItemClickListener(this); - registerForContextMenu(listView); - adapter = createListAdapter(); - setListAdapter(adapter); - } - - /** - * Inflates layout. - * - * @param savedInstanceState - */ - protected void onInflate(Bundle savedInstanceState) { - setContentView(R.layout.list); - } - - protected abstract T getSavedValue(Bundle bundle, String key); - - protected abstract void putSavedValue(Bundle bundle, String key, - T actionWith); - - protected abstract int getAddTextResourceId(); - - protected abstract Intent getAddIntent(); - - protected abstract Intent getEditIntent(T actionWith); - - protected abstract int getRemoveTextResourceId(); - - protected abstract String getRemoveConfirmation(T actionWith); - - protected abstract void removeItem(T actionWith); - - protected abstract BaseListEditorAdapter createListAdapter(); - - protected T getActionWith() { - return actionWith; - } - - @Override - protected void onResume() { - super.onResume(); - adapter.onChange(); - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - if (actionWith != null) - putSavedValue(outState, SAVED_ACTION_WITH, actionWith); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - menu.add(0, OPTION_MENU_ADD_ID, 0, getString(getAddTextResourceId())) - .setIcon(android.R.drawable.ic_menu_add) - .setIntent(getAddIntent()); - return true; - } - - @SuppressWarnings("unchecked") - @Override - public void onCreateContextMenu(ContextMenu menu, View v, - ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, v, menuInfo); - final AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; - actionWith = (T) getListView().getItemAtPosition(info.position); - if (actionWith == null) - // Add button - return; - onCreateContextMenu(menu, actionWith); - } - - protected void onCreateContextMenu(ContextMenu menu, T actionWith) { - menu.add(0, CONTEXT_MENU_DELETE_ID, 0, - getString(getRemoveTextResourceId())); - } - - @Override - public boolean onContextItemSelected(MenuItem item) { - if (super.onContextItemSelected(item)) - return true; - if (item.getItemId() == CONTEXT_MENU_DELETE_ID) { - showDialog(DIALOG_DELETE_ID); - return true; - } - return false; - } - - @Override - protected Dialog onCreateDialog(int id) { - Dialog dialog = super.onCreateDialog(id); - if (dialog != null) - return dialog; - if (id == DIALOG_DELETE_ID) - return new ConfirmDialogBuilder(this, DIALOG_DELETE_ID, this) - .setMessage(getRemoveConfirmation(actionWith)).create(); - return null; - } - - @SuppressWarnings("unchecked") - @Override - public void onItemClick(AdapterView parent, View view, int position, - long id) { - T actionWith = (T) parent.getAdapter().getItem(position); - Intent intent; - if (actionWith == null) - intent = getAddIntent(); - else - intent = getEditIntent(actionWith); - startActivity(intent); - } - - @Override - public void onAccept(DialogBuilder dialogBuilder) { - switch (dialogBuilder.getDialogId()) { - case DIALOG_DELETE_ID: - removeItem(actionWith); - adapter.onChange(); - break; - } - } - - @Override - public void onDecline(DialogBuilder dialogBuilder) { - } - - @Override - public void onCancel(DialogBuilder dialogBuilder) { - } - -} diff --git a/app/src/main/java/com/xabber/android/ui/helper/BaseSettingsActivity.java b/app/src/main/java/com/xabber/android/ui/helper/BaseSettingsActivity.java deleted file mode 100644 index f57564bd3e..0000000000 --- a/app/src/main/java/com/xabber/android/ui/helper/BaseSettingsActivity.java +++ /dev/null @@ -1,338 +0,0 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.xabber.android.ui.helper; - -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; - -import android.app.AlertDialog; -import android.app.Dialog; -import android.content.DialogInterface; -import android.net.Uri; -import android.os.Bundle; -import android.preference.CheckBoxPreference; -import android.preference.EditTextPreference; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.PreferenceScreen; -import android.view.KeyEvent; -import android.view.Menu; -import android.view.MenuItem; - -import com.xabber.android.ui.widget.RingtonePreference; -import com.xabber.androiddev.R; - -/** - * Provide possibility to edit, apply and discard settings. - * - * String resource id is used to identify preferences. - * - * @author alexander.ivanov - * - * @param - */ -public abstract class BaseSettingsActivity extends ManagedPreferenceActivity - implements Preference.OnPreferenceChangeListener { - - private static final int MENU_SAVE = Menu.FIRST; - private static final int MENU_CANCEL = Menu.FIRST + 1; - private static final int CONFIRM_DIALOG_ID = 0; - - /** - * Set of initial values is in progress. - */ - private boolean initialChange; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (isFinishing()) - return; - onInflate(savedInstanceState); - if (isFinishing()) - return; - PreferenceSummaryHelper.updateSummary(getPreferenceScreen()); - if (savedInstanceState == null) - operation(Operation.read); - PreferenceScreen preferenceScreen = getPreferenceScreen(); - initialChange = true; - for (int index = 0; index < preferenceScreen.getPreferenceCount(); index++) { - Preference preference = preferenceScreen.getPreference(index); - preference.setOnPreferenceChangeListener(this); - if (preference instanceof EditTextPreference) - onPreferenceChange(preference, - ((EditTextPreference) preference).getText()); - else if (preference instanceof CheckBoxPreference) - onPreferenceChange(preference, - ((CheckBoxPreference) preference).isChecked()); - else if (preference instanceof ListPreference) - onPreferenceChange(preference, - ((ListPreference) preference).getValue()); - } - initialChange = false; - } - - protected boolean isInitialChange() { - return initialChange; - } - - /** - * Inflates layout. - * - * @param savedInstanceState - */ - protected abstract void onInflate(Bundle savedInstanceState); - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - menu.add(0, MENU_SAVE, 0, android.R.string.ok).setIcon( - android.R.drawable.ic_menu_save); - menu.add(0, MENU_CANCEL, 0, android.R.string.cancel).setIcon( - android.R.drawable.ic_menu_close_clear_cancel); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case MENU_SAVE: - if (operation(Operation.save)) - finish(); - return true; - case MENU_CANCEL: - if (operation(Operation.discard)) - finish(); - else - showDialog(CONFIRM_DIALOG_ID); - return true; - } - return super.onOptionsItemSelected(item); - } - - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - if (preference instanceof EditTextPreference) - preference.setSummary((String) newValue); - return true; - } - - @Override - protected Dialog onCreateDialog(int id) { - if (id == CONFIRM_DIALOG_ID) { - return new AlertDialog.Builder(this) - .setTitle(android.R.string.dialog_alert_title) - .setIcon(android.R.drawable.ic_dialog_alert) - .setMessage(R.string.confirm_cancellation) - .setPositiveButton(android.R.string.yes, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, - int w) { - finish(); - } - }).setNegativeButton(android.R.string.no, null) - .create(); - } - return super.onCreateDialog(id); - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - switch (keyCode) { - case KeyEvent.KEYCODE_BACK: - if (operation(Operation.save)) - finish(); - return true; - } - return super.onKeyDown(keyCode, event); - } - - /** - * Possible operations. - */ - private static enum Operation { - save, discard, read - }; - - protected void putValue(Map map, int resoureId, Object value) { - map.put(getString(resoureId), value); - } - - protected String getString(Map map, int resoureId) { - return (String) map.get(getString(resoureId)); - } - - protected int getInt(Map map, int resoureId) { - return (Integer) map.get(getString(resoureId)); - } - - protected boolean getBoolean(Map map, int resoureId) { - return (Boolean) map.get(getString(resoureId)); - } - - protected Uri getUri(Map map, int resoureId) { - return (Uri) map.get(getString(resoureId)); - } - - /** - * @param intent - * @return Whether an operation succeed. - */ - private boolean operation(Operation selected) { - Map source = getValues(); - if (selected == Operation.read) - setPreferences(source); - else { - Map result = getPreferences(source); - for (Entry entry : source.entrySet()) - if (!result.containsKey(entry.getKey())) - result.put(entry.getKey(), entry.getValue()); - if (selected == Operation.save) - return setValues(source, result); - else if (selected == Operation.discard) { - for (String key : source.keySet()) - if (hasChanges(source, result, key)) - return false; - } else - throw new IllegalStateException(); - } - return true; - } - - /** - * @param source - * @param result - * @param key - * @return Whether value has been changed. - */ - protected boolean hasChanges(Map source, - Map result, String key) { - Object sourceValue = source.get(key); - Object targetValue = result.get(key); - return (sourceValue == null && targetValue != null) - || (sourceValue != null && !sourceValue.equals(targetValue)); - } - - /** - * @param source - * @param result - * @param resourceId - * @return Whether value has been changed. - */ - protected boolean hasChanges(Map source, - Map result, int resourceId) { - return hasChanges(source, result, getString(resourceId)); - } - - /** - * @return Map with source values. - */ - protected abstract Map getValues(); - - /** - * Set values to the UI elements. - * - * @param source - */ - protected void setPreferences(Map source) { - PreferenceScreen preferenceScreen = getPreferenceScreen(); - for (int index = 0; index < preferenceScreen.getPreferenceCount(); index++) { - Preference preference = preferenceScreen.getPreference(index); - Object value = source.get(preference.getKey()); - setPreference(preference, value); - } - } - - /** - * Set value to the UI element. - * - * @param preference - * @param value - */ - protected void setPreference(Preference preference, Object value) { - if (preference instanceof EditTextPreference) - ((EditTextPreference) preference) - .setText(value instanceof Integer ? String.valueOf(value) - : (String) value); - else if (preference instanceof CheckBoxPreference) - ((CheckBoxPreference) preference).setChecked((Boolean) value); - else if (preference instanceof ListPreference) - ((ListPreference) preference).setValueIndex((Integer) value); - else if (preference instanceof RingtonePreference) - ((RingtonePreference) preference).setUri((Uri) value); - } - - /** - * Get values from the UI elements. - * - * @param source - * @return - */ - protected Map getPreferences(Map source) { - Map result = new HashMap(); - PreferenceScreen preferenceScreen = getPreferenceScreen(); - for (int index = 0; index < preferenceScreen.getPreferenceCount(); index++) { - Preference preference = preferenceScreen.getPreference(index); - result.put(preference.getKey(), getPrefecence(preference, source)); - } - return result; - } - - /** - * Get value from the UI element. - * - * @param preference - * @param source - * @return - */ - protected Object getPrefecence(Preference preference, - Map source) { - if (preference instanceof PreferenceScreen) - return null; - else if (preference instanceof EditTextPreference) { - String value = ((EditTextPreference) preference).getText(); - if (source.get(preference.getKey()) instanceof Integer) - try { - return Integer.parseInt(value); - } catch (Exception NumberFormatException) { - return null; - } - else - return value; - } else if (preference instanceof CheckBoxPreference) - return ((CheckBoxPreference) preference).isChecked(); - else if (preference instanceof ListPreference) - return Integer - .valueOf(((ListPreference) preference) - .findIndexOfValue(((ListPreference) preference) - .getValue())); - else if (preference instanceof RingtonePreference) - return ((RingtonePreference) preference).getUri(); - throw new IllegalStateException(); - } - - /** - * Apply result values. - * - * @param source - * @param result - * @return Whether operation succeed. - */ - protected abstract boolean setValues(Map source, - Map result); - -} diff --git a/app/src/main/java/com/xabber/android/ui/helper/ContactTitleActionBarInflater.java b/app/src/main/java/com/xabber/android/ui/helper/ContactTitleActionBarInflater.java new file mode 100644 index 0000000000..4842508bdc --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/helper/ContactTitleActionBarInflater.java @@ -0,0 +1,59 @@ +package com.xabber.android.ui.helper; + +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.TextView; + +import com.xabber.android.R; +import com.xabber.android.data.roster.AbstractContact; + +public class ContactTitleActionBarInflater { + + private final AppCompatActivity activity; + private final Toolbar toolbar; + private View actionBarView; + + + private BarPainter barPainter; + + public ContactTitleActionBarInflater(AppCompatActivity activity, Toolbar toolbar) { + this.activity = activity; + this.toolbar = toolbar; + } + + public void setUpActionBarView() { + + barPainter = new BarPainter(activity, toolbar); + + activity.setSupportActionBar(toolbar); + ActionBar actionBar = activity.getSupportActionBar(); + actionBar.setDisplayShowHomeEnabled(false); + actionBar.setDisplayHomeAsUpEnabled(true); + + actionBarView = LayoutInflater.from(activity).inflate(R.layout.contact_title, null); + + actionBar.setCustomView(actionBarView, new ActionBar.LayoutParams( + ActionBar.LayoutParams.MATCH_PARENT, ActionBar.LayoutParams.MATCH_PARENT)); + } + + public void update(AbstractContact abstractContact) { + barPainter.updateWithAccountName(abstractContact.getAccount()); + + activity.getSupportActionBar().setDisplayShowCustomEnabled(true); + activity.getSupportActionBar().setDisplayShowTitleEnabled(false); + actionBarView.setVisibility(View.VISIBLE); + + ContactTitleInflater.updateTitle(actionBarView, activity, abstractContact); + } + + public void setStatusText(String user) { + ((TextView) actionBarView.findViewById(R.id.status_text)).setText(user); + } + + public void hideStatusIcon() { + actionBarView.findViewById(R.id.status_icon).setVisibility(View.GONE); + } +} diff --git a/app/src/main/java/com/xabber/android/ui/helper/ContactTitleExpandableToolbarInflater.java b/app/src/main/java/com/xabber/android/ui/helper/ContactTitleExpandableToolbarInflater.java new file mode 100644 index 0000000000..30fc5f6a90 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/helper/ContactTitleExpandableToolbarInflater.java @@ -0,0 +1,189 @@ +package com.xabber.android.ui.helper; + + +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.drawable.ColorDrawable; +import android.os.Build; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.util.TypedValue; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.widget.RelativeLayout; + +import com.github.ksoichiro.android.observablescrollview.ObservableScrollView; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollState; +import com.github.ksoichiro.android.observablescrollview.ScrollUtils; +import com.xabber.android.R; +import com.xabber.android.data.account.AccountManager; +import com.xabber.android.data.roster.AbstractContact; + +import static java.lang.Math.pow; +import static java.lang.Math.round; +import static java.lang.Math.sqrt; + +public class ContactTitleExpandableToolbarInflater implements ObservableScrollViewCallbacks { + + private final AppCompatActivity activity; + + private View avatarView; + private View titleView; + private View contactNamePanel; + + private int toolbarHeight; + private int paddingLeftMin; + private int actionBarSize; + private int toolbarHeightDelta; + private int avatarLargeSize; + private int avatarNormalSize; + private int avatarRadius; + private int contactTitlePaddingBottomBig; + private int contactTitlePaddingBottomSmall; + private Toolbar toolbar; + + public ContactTitleExpandableToolbarInflater(AppCompatActivity activity) { + this.activity = activity; + } + + public void onCreate(AbstractContact abstractContact) { + activity.setContentView(R.layout.expandable_contact_title_activity); + toolbar = (Toolbar) activity.findViewById(R.id.toolbar_overlay); + + avatarView = activity.findViewById(R.id.avatar); + contactNamePanel = activity.findViewById(R.id.contact_name_panel); + + titleView = activity.findViewById(R.id.expandable_contact_title); + + int[] accountActionBarColors = activity.getResources().getIntArray(R.array.account_action_bar); + + titleView.setBackgroundDrawable(new ColorDrawable(accountActionBarColors[ + AccountManager.getInstance().getColorLevel(abstractContact.getAccount())])); + + ContactTitleInflater.updateTitle(titleView, activity, abstractContact); + + int[] accountStatusBarColors = activity.getResources().getIntArray(R.array.account_status_bar); + int colorLevel = AccountManager.getInstance().getColorLevel(abstractContact.getAccount()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + Window window = activity.getWindow(); + window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + window.setStatusBarColor(accountStatusBarColors[colorLevel]); + } + } + + public void onResume() { + Resources resources = activity.getResources(); + paddingLeftMin = resources.getDimensionPixelSize(R.dimen.contact_title_padding_left); + avatarLargeSize = resources.getDimensionPixelSize(R.dimen.avatar_large_size); + avatarNormalSize = resources.getDimensionPixelSize(R.dimen.avatar_normal_size); + avatarRadius = resources.getDimensionPixelSize(R.dimen.avatar_radius); + contactTitlePaddingBottomBig = resources.getDimensionPixelSize(R.dimen.contact_title_padding_bottom_big); + contactTitlePaddingBottomSmall = resources.getDimensionPixelSize(R.dimen.contact_title_padding_bottom_small); + toolbarHeight = resources.getDimensionPixelSize(R.dimen.toolbar_height); + + actionBarSize = getActionBarSize(); + toolbarHeightDelta = toolbarHeight - actionBarSize; + + final ObservableScrollView scrollView = (ObservableScrollView) activity.findViewById(R.id.scroll); + scrollView.setScrollViewCallbacks(this); + + ScrollUtils.addOnGlobalLayoutListener(activity.findViewById(R.id.expandable_contact_title), new Runnable() { + @Override + public void run() { + updateFlexibleSpaceText(scrollView.getCurrentScrollY()); + } + }); + } + + protected int getActionBarSize() { + TypedValue typedValue = new TypedValue(); + int[] textSizeAttr = new int[]{R.attr.actionBarSize}; + int indexOfAttrTextSize = 0; + TypedArray a = activity.obtainStyledAttributes(typedValue.data, textSizeAttr); + int actionBarSize = a.getDimensionPixelSize(indexOfAttrTextSize, -1); + a.recycle(); + return actionBarSize; + } + + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + updateFlexibleSpaceText(scrollY); + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + } + + private void updateFlexibleSpaceText(final int scrollY) { + setLeftPadding(scrollY); + setTopPadding(scrollY); + setAvatarSize(scrollY); + setHeight(scrollY); + } + + private void setTopPadding(int scrollY) { + int paddingDelta = contactTitlePaddingBottomBig - contactTitlePaddingBottomSmall; + int paddingBottom = contactTitlePaddingBottomBig - scrollY * paddingDelta / toolbarHeightDelta; + + if (scrollY <= 0) { + paddingBottom = contactTitlePaddingBottomBig; + } + + if (scrollY >= toolbarHeightDelta) { + paddingBottom = contactTitlePaddingBottomSmall; + } + + contactNamePanel.setPadding(0, 0, 0, paddingBottom); + } + + private void setAvatarSize(int scrollY) { + int newAvatarSize = avatarLargeSize - (scrollY / 2); + + if (newAvatarSize < avatarNormalSize) { + newAvatarSize = avatarNormalSize; + } + + if (avatarView.getWidth() != newAvatarSize) { + avatarView.getLayoutParams().width = newAvatarSize; + avatarView.getLayoutParams().height = newAvatarSize; + } + } + + private void setHeight(int scrollY) { + int newHeight = toolbarHeight - scrollY; + if (newHeight < actionBarSize) { + newHeight = actionBarSize; + } + + titleView.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, newHeight)); + } + + private void setLeftPadding(int scrollY) { + int paddingLeft = (int) round(sqrt(pow(avatarRadius, 2) - pow(scrollY - avatarRadius, 2))); + + if (scrollY < 0) { + paddingLeft = paddingLeftMin; + } + + if (scrollY > avatarRadius) { + paddingLeft = avatarRadius; + } + + if (paddingLeft < paddingLeftMin) { + paddingLeft = paddingLeftMin; + } + + titleView.setPadding(paddingLeft, 0, paddingLeft, 0); + } + + public Toolbar getToolbar() { + return toolbar; + } +} diff --git a/app/src/main/java/com/xabber/android/ui/helper/ContactTitleInflater.java b/app/src/main/java/com/xabber/android/ui/helper/ContactTitleInflater.java index d3fb4e7a83..70120d6006 100644 --- a/app/src/main/java/com/xabber/android/ui/helper/ContactTitleInflater.java +++ b/app/src/main/java/com/xabber/android/ui/helper/ContactTitleInflater.java @@ -1,106 +1,74 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.ui.helper; -import org.jivesoftware.smackx.ChatState; - -import android.app.Activity; -import android.content.res.TypedArray; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Shader.TileMode; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; +import android.content.Context; import android.view.View; -import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; -import com.xabber.android.data.account.AccountManager; +import com.xabber.android.R; import com.xabber.android.data.extension.cs.ChatStateManager; import com.xabber.android.data.roster.AbstractContact; -import com.xabber.android.utils.Emoticons; -import com.xabber.androiddev.R; -/** - * Helper class to update contact_title.xml. - * - * @author alexander.ivanov - * - */ +import org.jivesoftware.smackx.ChatState; + public class ContactTitleInflater { - /** - * Fill title with information about {@link AbstractContact} and provides - * back button callback. - * - * @param titleView - * @param activity - * @param abstractContact - */ - public static void updateTitle(View titleView, final Activity activity, - AbstractContact abstractContact) { - final TypedArray typedArray = activity - .obtainStyledAttributes(R.styleable.ContactList); - final Drawable titleAccountBackground = typedArray - .getDrawable(R.styleable.ContactList_titleAccountBackground); - typedArray.recycle(); - final TextView nameView = (TextView) titleView.findViewById(R.id.name); - final ImageView avatarView = (ImageView) titleView - .findViewById(R.id.avatar); - final ImageView statusModeView = (ImageView) titleView - .findViewById(R.id.status_mode); - final TextView statusTextView = (TextView) titleView - .findViewById(R.id.status_text); - final View shadowView = titleView.findViewById(R.id.shadow); - final ImageButton backButton = (ImageButton) titleView - .findViewById(R.id.back_button); - titleView.setBackgroundDrawable(titleAccountBackground); - nameView.setText(abstractContact.getName()); - statusModeView.setImageLevel(abstractContact.getStatusMode() - .getStatusLevel()); - titleView.getBackground().setLevel( - AccountManager.getInstance().getColorLevel( - abstractContact.getAccount())); - avatarView.setImageDrawable(abstractContact.getAvatar()); - ChatState chatState = ChatStateManager.getInstance().getChatState( - abstractContact.getAccount(), abstractContact.getUser()); - final CharSequence statusText; - if (chatState == ChatState.composing) - statusText = activity.getString(R.string.chat_state_composing); - else if (chatState == ChatState.paused) - statusText = activity.getString(R.string.chat_state_paused); - else - statusText = Emoticons.getSmiledText(activity, - abstractContact.getStatusText()); - statusTextView.setText(statusText); - final Bitmap bitmap = BitmapFactory.decodeResource( - activity.getResources(), R.drawable.shadow); - final BitmapDrawable shadowDrawable = new BitmapDrawable(bitmap); - shadowDrawable.setTileModeXY(TileMode.REPEAT, TileMode.REPEAT); - shadowView.setBackgroundDrawable(shadowDrawable); - if (abstractContact.isConnected()) - shadowView.setVisibility(View.GONE); - else - shadowView.setVisibility(View.VISIBLE); - backButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - activity.finish(); - } - }); - } + public static void updateTitle(View titleView, final Context context, AbstractContact abstractContact) { + final TextView nameView = (TextView) titleView.findViewById(R.id.name); + final ImageView avatarView = (ImageView) titleView.findViewById(R.id.avatar); + + nameView.setText(abstractContact.getName()); + avatarView.setImageDrawable(abstractContact.getAvatar()); + setStatus(context, titleView, abstractContact); + } + + private static void setStatus(Context context, View titleView, AbstractContact abstractContact) { + final ImageView statusModeView = (ImageView) titleView.findViewById(R.id.status_icon); + + int statusLevel = abstractContact.getStatusMode().getStatusLevel(); + if (isContactOffline(statusLevel)) { + statusModeView.setVisibility(View.GONE); + } else { + statusModeView.setVisibility(View.VISIBLE); + statusModeView.setImageLevel(statusLevel); + } + + final TextView statusTextView = (TextView) titleView.findViewById(R.id.status_text); + + + ChatState chatState = ChatStateManager.getInstance().getChatState( + abstractContact.getAccount(), abstractContact.getUser()); + + CharSequence statusText; + if (chatState == ChatState.composing) { + statusText = context.getString(R.string.chat_state_composing); + } else if (chatState == ChatState.paused) { + statusText = context.getString(R.string.chat_state_paused); + } else { + statusText = abstractContact.getStatusText().trim(); + if (statusText.toString().isEmpty()) { + statusText = context.getString(abstractContact.getStatusMode().getStringID()); + } + } + statusTextView.setText(statusText); + } + + private static boolean isContactOffline(int statusLevel) { + return statusLevel == 6; + } } diff --git a/app/src/main/java/com/xabber/android/ui/helper/ContextMenuHelper.java b/app/src/main/java/com/xabber/android/ui/helper/ContextMenuHelper.java new file mode 100644 index 0000000000..e67939d9cc --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/helper/ContextMenuHelper.java @@ -0,0 +1,369 @@ +/** + * Copyright (c) 2013, Redsolution LTD. All rights reserved. + * + * This file is part of Xabber project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License, Version 3. + * + * Xabber is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License, + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.xabber.android.ui.helper; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.support.v4.app.FragmentActivity; +import android.view.ContextMenu; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; + +import com.xabber.android.R; +import com.xabber.android.data.Application; +import com.xabber.android.data.NetworkException; +import com.xabber.android.data.SettingsManager; +import com.xabber.android.data.account.AccountItem; +import com.xabber.android.data.account.AccountManager; +import com.xabber.android.data.account.StatusMode; +import com.xabber.android.data.connection.ConnectionState; +import com.xabber.android.data.extension.muc.MUCManager; +import com.xabber.android.data.message.MessageManager; +import com.xabber.android.data.notification.NotificationManager; +import com.xabber.android.data.roster.AbstractContact; +import com.xabber.android.data.roster.GroupManager; +import com.xabber.android.data.roster.PresenceManager; +import com.xabber.android.data.roster.ShowOfflineMode; +import com.xabber.android.ui.ChatViewer; +import com.xabber.android.ui.ConferenceAdd; +import com.xabber.android.ui.ContactAdd; +import com.xabber.android.ui.ContactEditor; +import com.xabber.android.ui.ContactViewer; +import com.xabber.android.ui.GroupEditor; +import com.xabber.android.ui.StatusEditor; +import com.xabber.android.ui.adapter.UpdatableAdapter; +import com.xabber.android.ui.dialog.ContactDeleteDialogFragment; +import com.xabber.android.ui.dialog.GroupDeleteDialogFragment; +import com.xabber.android.ui.dialog.GroupRenameDialogFragment; +import com.xabber.android.ui.dialog.MUCDeleteDialogFragment; +import com.xabber.android.ui.preferences.AccountEditor; + +/** + * Helper class for context menu creation. + * + * @author alexander.ivanov + */ +public class ContextMenuHelper { + + private ContextMenuHelper() { + } + + public static void createContactContextMenu(final FragmentActivity activity, + final UpdatableAdapter adapter, AbstractContact abstractContact, ContextMenu menu) { + final String account = abstractContact.getAccount(); + final String user = abstractContact.getUser(); + menu.setHeaderTitle(abstractContact.getName()); + MenuInflater inflater = activity.getMenuInflater(); + inflater.inflate(R.menu.contact_list_contact_context_menu, menu); + + setContactContextMenuActions(activity, adapter, menu, account, user); + setContactContextMenuItemsVisibilty(abstractContact, menu, account, user); + } + + private static void setContactContextMenuActions(final FragmentActivity activity, + final UpdatableAdapter adapter, + ContextMenu menu, + final String account, final String user) { + menu.findItem(R.id.action_chat).setOnMenuItemClickListener( + new MenuItem.OnMenuItemClickListener() { + + @Override + public boolean onMenuItemClick(MenuItem item) { + MessageManager.getInstance().openChat(account, user); + activity.startActivity(ChatViewer.createSpecificChatIntent( + activity, account, user)); + return true; + } + }); + + menu.findItem(R.id.action_edit_conference).setIntent( + ConferenceAdd.createIntent(activity, account, user)); + + menu.findItem(R.id.action_delete_conference).setOnMenuItemClickListener( + new MenuItem.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + MUCDeleteDialogFragment.newInstance(account, user) + .show(activity.getFragmentManager(), "MUC_DELETE"); + return true; + } + }); + + menu.findItem(R.id.action_join_conference).setOnMenuItemClickListener( + new MenuItem.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + MUCManager.getInstance().joinRoom(account, + user, true); + return true; + } + }); + + menu.findItem(R.id.action_leave_conference).setOnMenuItemClickListener( + new MenuItem.OnMenuItemClickListener() { + + @Override + public boolean onMenuItemClick(MenuItem item) { + MUCManager.getInstance().leaveRoom(account, user); + MessageManager.getInstance().closeChat(account, user); + NotificationManager.getInstance().removeMessageNotification(account, user); + adapter.onChange(); + return true; + } + + }); + + menu.findItem(R.id.action_contact_info).setIntent( + ContactEditor.createIntent(activity, account, user)); + menu.findItem(R.id.action_edit_contact_groups).setIntent( + GroupEditor.createIntent(activity, account, user)); + + menu.findItem(R.id.action_delete_contact).setOnMenuItemClickListener( + new MenuItem.OnMenuItemClickListener() { + + @Override + public boolean onMenuItemClick(MenuItem item) { + ContactDeleteDialogFragment.newInstance(account, + user).show(activity.getFragmentManager(), "CONTACT_DELETE"); + return true; + } + + }); + + menu.findItem(R.id.action_close_chat).setOnMenuItemClickListener( + new MenuItem.OnMenuItemClickListener() { + + @Override + public boolean onMenuItemClick(MenuItem item) { + MessageManager.getInstance().closeChat(account, + user); + NotificationManager.getInstance() + .removeMessageNotification(account, + user); + adapter.onChange(); + return true; + } + + }); + + menu.findItem(R.id.action_request_subscription) + .setOnMenuItemClickListener( + new MenuItem.OnMenuItemClickListener() { + + @Override + public boolean onMenuItemClick(MenuItem item) { + try { + PresenceManager.getInstance() + .requestSubscription( + account, user); + } catch (NetworkException e) { + Application.getInstance() + .onError(e); + } + return true; + } + + }); + + menu.findItem(R.id.action_accept_subscription).setOnMenuItemClickListener( + new MenuItem.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + try { + PresenceManager.getInstance().acceptSubscription(account, user); + } catch (NetworkException e) { + Application.getInstance().onError(e); + } + activity.startActivity(GroupEditor.createIntent(activity, account, user)); + return true; + } + }); + menu.findItem(R.id.action_discard_subscription).setOnMenuItemClickListener( + new MenuItem.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + try { + PresenceManager.getInstance() + .discardSubscription(account, user); + } catch (NetworkException e) { + Application.getInstance().onError(e); + } + return true; + } + }); + } + + private static void setContactContextMenuItemsVisibilty(AbstractContact abstractContact, + ContextMenu menu, + String account, String user) { + // all menu items are visible by default + // it allows to hide items in xml file without touching code + + if (!MUCManager.getInstance().hasRoom(account, user)) { + // is not conference + + menu.findItem(R.id.action_edit_conference).setVisible(false); + menu.findItem(R.id.action_delete_conference).setVisible(false); + menu.findItem(R.id.action_leave_conference).setVisible(false); + menu.findItem(R.id.action_join_conference).setVisible(false); + + if (!MessageManager.getInstance().hasActiveChat(account, user)) { + menu.findItem(R.id.action_close_chat).setVisible(false); + } + if (abstractContact.getStatusMode() != StatusMode.unsubscribed) { + menu.findItem(R.id.action_request_subscription).setVisible(false); + } + } else { // is conference + + menu.findItem(R.id.action_contact_info).setVisible(false); + menu.findItem(R.id.action_edit_contact_groups).setVisible(false); + menu.findItem(R.id.action_delete_contact).setVisible(false); + menu.findItem(R.id.action_close_chat).setVisible(false); + menu.findItem(R.id.action_request_subscription).setVisible(false); + + if (MUCManager.getInstance().inUse(account, user)) { + menu.findItem(R.id.action_edit_conference).setVisible(false); + } + + if (MUCManager.getInstance().isDisabled(account, user)) { + menu.findItem(R.id.action_leave_conference).setVisible(false); + } else { + menu.findItem(R.id.action_join_conference).setVisible(false); + } + + } + + if (!PresenceManager.getInstance().hasSubscriptionRequest(account, user)) { + menu.findItem(R.id.action_accept_subscription).setVisible(false); + menu.findItem(R.id.action_discard_subscription).setVisible(false); + } + } + + public static void createGroupContextMenu(final FragmentActivity activity, + final UpdatableAdapter adapter, final String account, final String group, ContextMenu menu) { + menu.setHeaderTitle(GroupManager.getInstance().getGroupName(account, group)); + if (!group.equals(GroupManager.ACTIVE_CHATS) && !group.equals(GroupManager.IS_ROOM)) { + menu.add(R.string.group_rename).setOnMenuItemClickListener( + new MenuItem.OnMenuItemClickListener() { + + @Override + public boolean onMenuItemClick(MenuItem item) { + GroupRenameDialogFragment.newInstance( + account.equals(GroupManager.NO_ACCOUNT) ? null : account, + group.equals(GroupManager.NO_GROUP) ? null + : group).show(activity.getFragmentManager(), + "GROUP_RENAME"); + return true; + } + }); + if (!group.equals(GroupManager.NO_GROUP)) { + menu.add(R.string.group_remove).setOnMenuItemClickListener( + new MenuItem.OnMenuItemClickListener() { + + @Override + public boolean onMenuItemClick(MenuItem item) { + GroupDeleteDialogFragment.newInstance( + account.equals(GroupManager.NO_ACCOUNT) ? null : account, group) + .show(activity.getFragmentManager(), "GROUP_DELETE"); + return true; + } + }); + } + } + if (!group.equals(GroupManager.ACTIVE_CHATS)) { + menu.add(R.string.show_offline_settings).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + createOfflineContactsDialog(activity, adapter, account, group).show(); + return true; + } + }); + } + } + + public static void createAccountContextMenu( final FragmentActivity activity, final UpdatableAdapter adapter, + final String account, ContextMenu menu) { + activity.getMenuInflater().inflate(R.menu.account, menu); + menu.setHeaderTitle(AccountManager.getInstance().getVerboseName(account)); + + setUpAccountMenu(activity, adapter, account, menu); + } + + public static void setUpAccountMenu(final FragmentActivity activity, final UpdatableAdapter adapter, final String account, Menu menu) { + final AccountItem accountItem = AccountManager.getInstance().getAccount(account); + ConnectionState state = accountItem.getState(); + + if (state == ConnectionState.waiting) { + menu.findItem(R.id.action_reconnect_account).setVisible(true).setOnMenuItemClickListener( + new MenuItem.OnMenuItemClickListener() { + + @Override + public boolean onMenuItemClick(MenuItem item) { + if (accountItem.updateConnection(true)) + AccountManager.getInstance().onAccountChanged(account); + return true; + } + + }); + } + + menu.findItem(R.id.action_edit_account_status).setIntent(StatusEditor.createIntent(activity, account)); + menu.findItem(R.id.action_edit_account).setIntent(AccountEditor.createIntent(activity, account)); + + if (state.isConnected()) { + menu.findItem(R.id.action_contact_info).setVisible(true).setOnMenuItemClickListener( + new MenuItem.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + activity.startActivity(ContactViewer.createIntent(activity, account, GroupManager.IS_ACCOUNT)); + return true; + } + }); + menu.findItem(R.id.action_add_contact).setVisible(true).setIntent(ContactAdd.createIntent(activity, account)); + } + + if (SettingsManager.contactsShowAccounts()) { + menu.findItem(R.id.action_set_up_offline_contacts).setVisible(true) + .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + ContextMenuHelper.createOfflineContactsDialog(activity, adapter, + account, GroupManager.IS_ACCOUNT).show(); + return true; + } + }); + } + } + + public static AlertDialog createOfflineContactsDialog(Context context, final UpdatableAdapter adapter, + final String account, final String group) { + return new AlertDialog.Builder(context) + .setTitle(R.string.show_offline_settings) + .setSingleChoiceItems( + R.array.offline_contacts_show_option, + GroupManager.getInstance().getShowOfflineMode(account, group).ordinal(), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + GroupManager.getInstance().setShowOfflineMode(account, + group, ShowOfflineMode.values()[which]); + adapter.onChange(); + dialog.dismiss(); + } + }).create(); + } +} diff --git a/app/src/main/java/com/xabber/android/ui/helper/DummyAvatarInflaterHelper.java b/app/src/main/java/com/xabber/android/ui/helper/DummyAvatarInflaterHelper.java deleted file mode 100644 index 40e876c9bf..0000000000 --- a/app/src/main/java/com/xabber/android/ui/helper/DummyAvatarInflaterHelper.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.xabber.android.ui.helper; - -import android.widget.ImageView; - -import com.xabber.android.data.roster.AbstractContact; - -/** - * This dummy implementation do nothing. - * - * @author alexander.ivanov - * - */ -public class DummyAvatarInflaterHelper extends AbstractAvatarInflaterHelper { - - @Override - public void updateAvatar(ImageView avatar, AbstractContact abstractContact) { - } - -} diff --git a/app/src/main/java/com/xabber/android/ui/helper/ManagedActivity.java b/app/src/main/java/com/xabber/android/ui/helper/ManagedActivity.java index 179edd2a90..bed08d9f14 100644 --- a/app/src/main/java/com/xabber/android/ui/helper/ManagedActivity.java +++ b/app/src/main/java/com/xabber/android/ui/helper/ManagedActivity.java @@ -1,82 +1,81 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.ui.helper; -import android.app.Activity; import android.content.Intent; import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; import com.xabber.android.data.ActivityManager; /** * Base class for all Activities. - * + *

* Adds custom activity logic. - * + * * @author alexander.ivanov - * */ -public abstract class ManagedActivity extends Activity { +public abstract class ManagedActivity extends AppCompatActivity { - @Override - protected void onCreate(Bundle savedInstanceState) { - ActivityManager.getInstance().onCreate(this); - super.onCreate(savedInstanceState); - } + @Override + protected void onCreate(Bundle savedInstanceState) { + ActivityManager.getInstance().onCreate(this); + super.onCreate(savedInstanceState); + } - @Override - protected void onResume() { - ActivityManager.getInstance().onResume(this); - super.onResume(); - } + @Override + protected void onResume() { + ActivityManager.getInstance().onResume(this); + super.onResume(); + } - @Override - protected void onPause() { - ActivityManager.getInstance().onPause(this); - super.onPause(); - } + @Override + protected void onPause() { + ActivityManager.getInstance().onPause(this); + super.onPause(); + } - @Override - protected void onDestroy() { - ActivityManager.getInstance().onDestroy(this); - super.onDestroy(); - } + @Override + protected void onDestroy() { + ActivityManager.getInstance().onDestroy(this); + super.onDestroy(); + } - @Override - protected void onNewIntent(Intent intent) { - ActivityManager.getInstance().onNewIntent(this, intent); - super.onNewIntent(intent); - } + @Override + protected void onNewIntent(Intent intent) { + ActivityManager.getInstance().onNewIntent(this, intent); + super.onNewIntent(intent); + } - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - ActivityManager.getInstance().onActivityResult(this, requestCode, - resultCode, data); - super.onActivityResult(requestCode, resultCode, data); - } + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + ActivityManager.getInstance().onActivityResult(this, requestCode, + resultCode, data); + super.onActivityResult(requestCode, resultCode, data); + } - @Override - public void startActivity(Intent intent) { - ActivityManager.getInstance().updateIntent(this, intent); - super.startActivity(intent); - } + @Override + public void startActivity(Intent intent) { + ActivityManager.getInstance().updateIntent(this, intent); + super.startActivity(intent); + } - @Override - public void startActivityForResult(Intent intent, int requestCode) { - ActivityManager.getInstance().updateIntent(this, intent); - super.startActivityForResult(intent, requestCode); - } + @Override + public void startActivityForResult(Intent intent, int requestCode) { + ActivityManager.getInstance().updateIntent(this, intent); + super.startActivityForResult(intent, requestCode); + } } diff --git a/app/src/main/java/com/xabber/android/ui/helper/ManagedDialog.java b/app/src/main/java/com/xabber/android/ui/helper/ManagedDialog.java index 78efc044b9..ca825a5722 100644 --- a/app/src/main/java/com/xabber/android/ui/helper/ManagedDialog.java +++ b/app/src/main/java/com/xabber/android/ui/helper/ManagedDialog.java @@ -1,106 +1,109 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.ui.helper; import android.os.Bundle; +import android.support.v7.widget.Toolbar; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; -import android.view.Window; import android.widget.TextView; -import com.xabber.androiddev.R; +import com.xabber.android.R; /** * Helper for dialog activities. - * + *

* Please don't call {@link #setContentView(int)} from your * {@link #onCreate(Bundle)}. - * + * * @author alexander.ivanov - * */ public abstract class ManagedDialog extends SingleActivity { - @Override - protected void onCreate(Bundle savedInstanceState) { - setTheme(android.R.style.Theme_Dialog); - super.onCreate(savedInstanceState); - requestWindowFeature(Window.FEATURE_NO_TITLE); - setContentView(R.layout.dialog); - findViewById(android.R.id.button1).setOnClickListener( - new OnClickListener() { - @Override - public void onClick(View v) { - onAccept(); - } - }); - findViewById(android.R.id.button2).setOnClickListener( - new OnClickListener() { - @Override - public void onClick(View v) { - onDecline(); - } - }); - findViewById(android.R.id.button3).setOnClickListener( - new OnClickListener() { - @Override - public void onClick(View v) { - onCenter(); - } - }); - } - - public void setDialogTitle(CharSequence title) { - ((TextView) findViewById(android.R.id.title)).setText(title); - } - - public void setDialogTitle(int resid) { - ((TextView) findViewById(android.R.id.title)).setText(resid); - } - - public void setDialogMessage(CharSequence title) { - ((TextView) findViewById(android.R.id.message)).setText(title); - } - - public void setDialogMessage(int resid) { - ((TextView) findViewById(android.R.id.message)).setText(resid); - } - - public void setCustomView(View view, boolean hideContainer) { - ((ViewGroup) findViewById(android.R.id.custom)).addView(view); - if (hideContainer) - findViewById(R.id.container).setVisibility(View.GONE); - } - - /** - * Click on first button. - */ - public void onAccept() { - } - - /** - * Click on second button. - */ - public void onDecline() { - } - - /** - * Click on center button. - */ - public void onCenter() { - } + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.dialog); + + setSupportActionBar((Toolbar) findViewById(R.id.toolbar_default)); + getSupportActionBar().setDisplayHomeAsUpEnabled(false); + + findViewById(android.R.id.button1).setOnClickListener( + new OnClickListener() { + @Override + public void onClick(View v) { + onAccept(); + } + }); + findViewById(android.R.id.button2).setOnClickListener( + new OnClickListener() { + @Override + public void onClick(View v) { + onDecline(); + } + }); + findViewById(android.R.id.button3).setOnClickListener( + new OnClickListener() { + @Override + public void onClick(View v) { + onCenter(); + } + }); + } + + public void setDialogTitle(CharSequence title) { + getSupportActionBar().setTitle(title); + } + + public void setDialogTitle(int resid) { + getSupportActionBar().setTitle(resid); + } + + public void setDialogMessage(CharSequence title) { + ((TextView) findViewById(android.R.id.message)).setText(title); + } + + public void setDialogMessage(int resid) { + ((TextView) findViewById(android.R.id.message)).setText(resid); + } + + public void setCustomView(View view, boolean hideContainer) { + ((ViewGroup) findViewById(android.R.id.custom)).addView(view); + if (hideContainer) { + findViewById(R.id.container).setVisibility(View.GONE); + } + } + + /** + * Click on first button. + */ + public void onAccept() { + } + + /** + * Click on second button. + */ + public void onDecline() { + } + + /** + * Click on center button. + */ + public void onCenter() { + } } diff --git a/app/src/main/java/com/xabber/android/ui/helper/ManagedListActivity.java b/app/src/main/java/com/xabber/android/ui/helper/ManagedListActivity.java index 184477a2cc..3b727b8cbd 100644 --- a/app/src/main/java/com/xabber/android/ui/helper/ManagedListActivity.java +++ b/app/src/main/java/com/xabber/android/ui/helper/ManagedListActivity.java @@ -1,82 +1,91 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.ui.helper; -import android.app.ListActivity; import android.content.Intent; import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.widget.ListAdapter; +import android.widget.ListView; import com.xabber.android.data.ActivityManager; /** * Base class for all ListActivities. - * + *

* Adds custom activity logic. - * + * * @author alexander.ivanov - * */ -public abstract class ManagedListActivity extends ListActivity { +public abstract class ManagedListActivity extends AppCompatActivity { - @Override - protected void onCreate(Bundle savedInstanceState) { - ActivityManager.getInstance().onCreate(this); - super.onCreate(savedInstanceState); - } + @Override + protected void onCreate(Bundle savedInstanceState) { + ActivityManager.getInstance().onCreate(this); + super.onCreate(savedInstanceState); + } - @Override - protected void onResume() { - ActivityManager.getInstance().onResume(this); - super.onResume(); - } + @Override + protected void onResume() { + ActivityManager.getInstance().onResume(this); + super.onResume(); + } - @Override - protected void onPause() { - ActivityManager.getInstance().onPause(this); - super.onPause(); - } + @Override + protected void onPause() { + ActivityManager.getInstance().onPause(this); + super.onPause(); + } - @Override - protected void onDestroy() { - ActivityManager.getInstance().onDestroy(this); - super.onDestroy(); - } + @Override + protected void onDestroy() { + ActivityManager.getInstance().onDestroy(this); + super.onDestroy(); + } - @Override - protected void onNewIntent(Intent intent) { - ActivityManager.getInstance().onNewIntent(this, intent); - super.onNewIntent(intent); - } + @Override + protected void onNewIntent(Intent intent) { + ActivityManager.getInstance().onNewIntent(this, intent); + super.onNewIntent(intent); + } - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - ActivityManager.getInstance().onActivityResult(this, requestCode, - resultCode, data); - super.onActivityResult(requestCode, resultCode, data); - } + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + ActivityManager.getInstance().onActivityResult(this, requestCode, + resultCode, data); + super.onActivityResult(requestCode, resultCode, data); + } - @Override - public void startActivity(Intent intent) { - ActivityManager.getInstance().updateIntent(this, intent); - super.startActivity(intent); - } + @Override + public void startActivity(Intent intent) { + ActivityManager.getInstance().updateIntent(this, intent); + super.startActivity(intent); + } - @Override - public void startActivityForResult(Intent intent, int requestCode) { - ActivityManager.getInstance().updateIntent(this, intent); - super.startActivityForResult(intent, requestCode); - } + @Override + public void startActivityForResult(Intent intent, int requestCode) { + ActivityManager.getInstance().updateIntent(this, intent); + super.startActivityForResult(intent, requestCode); + } + + public ListView getListView() { + return (ListView) findViewById(android.R.id.list); + } + + public void setListAdapter(ListAdapter adapter) { + getListView().setAdapter(adapter); + } } diff --git a/app/src/main/java/com/xabber/android/ui/helper/ManagedPreferenceActivity.java b/app/src/main/java/com/xabber/android/ui/helper/ManagedPreferenceActivity.java deleted file mode 100644 index 8ed0d5fd47..0000000000 --- a/app/src/main/java/com/xabber/android/ui/helper/ManagedPreferenceActivity.java +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.xabber.android.ui.helper; - -import android.content.Intent; -import android.os.Bundle; -import android.preference.PreferenceActivity; - -import com.xabber.android.data.ActivityManager; - -/** - * Base class for all PreferenceActivities. - * - * Adds custom activity logic. - * - * @author alexander.ivanov - * - */ -public abstract class ManagedPreferenceActivity extends PreferenceActivity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - ActivityManager.getInstance().onCreate(this); - super.onCreate(savedInstanceState); - } - - @Override - protected void onResume() { - ActivityManager.getInstance().onResume(this); - super.onResume(); - } - - @Override - protected void onPause() { - ActivityManager.getInstance().onPause(this); - super.onPause(); - } - - @Override - protected void onDestroy() { - ActivityManager.getInstance().onDestroy(this); - super.onDestroy(); - } - - @Override - protected void onNewIntent(Intent intent) { - ActivityManager.getInstance().onNewIntent(this, intent); - super.onNewIntent(intent); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - ActivityManager.getInstance().onActivityResult(this, requestCode, - resultCode, data); - super.onActivityResult(requestCode, resultCode, data); - } - - @Override - public void startActivity(Intent intent) { - ActivityManager.getInstance().updateIntent(this, intent); - super.startActivity(intent); - } - - @Override - public void startActivityForResult(Intent intent, int requestCode) { - ActivityManager.getInstance().updateIntent(this, intent); - super.startActivityForResult(intent, requestCode); - } - -} diff --git a/app/src/main/java/com/xabber/android/ui/helper/OrbotHelper.java b/app/src/main/java/com/xabber/android/ui/helper/OrbotHelper.java index 5728e27786..eecc305200 100644 --- a/app/src/main/java/com/xabber/android/ui/helper/OrbotHelper.java +++ b/app/src/main/java/com/xabber/android/ui/helper/OrbotHelper.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,18 +20,18 @@ public class OrbotHelper { - public final static String URI_ORBOT = "org.torproject.android"; + public final static String URI_ORBOT = "org.torproject.android"; - public static boolean isOrbotInstalled() { - PackageManager packageManager = Application.getInstance() - .getPackageManager(); - try { - packageManager.getPackageInfo(URI_ORBOT, - PackageManager.GET_ACTIVITIES); - return true; - } catch (PackageManager.NameNotFoundException e) { - return false; - } - } + public static boolean isOrbotInstalled() { + PackageManager packageManager = Application.getInstance() + .getPackageManager(); + try { + packageManager.getPackageInfo(URI_ORBOT, + PackageManager.GET_ACTIVITIES); + return true; + } catch (PackageManager.NameNotFoundException e) { + return false; + } + } } diff --git a/app/src/main/java/com/xabber/android/ui/helper/PreferenceSummaryHelper.java b/app/src/main/java/com/xabber/android/ui/helper/PreferenceSummaryHelper.java index b9a9ecdd40..fb31fcd7ff 100644 --- a/app/src/main/java/com/xabber/android/ui/helper/PreferenceSummaryHelper.java +++ b/app/src/main/java/com/xabber/android/ui/helper/PreferenceSummaryHelper.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,31 +20,52 @@ /** * Update preference's title and summary based on multiline title. - * + * * @author alexander.ivanov - * */ public final class PreferenceSummaryHelper { - private PreferenceSummaryHelper() { - } - - public static void updateSummary(PreferenceGroup group) { - for (int index = 0; index < group.getPreferenceCount(); index++) { - Preference preference = group.getPreference(index); - if (preference instanceof PreferenceGroup) - updateSummary((PreferenceGroup) preference); - String title = preference.getTitle().toString(); - int delimeter = title.indexOf("\n"); - if (delimeter == -1) - continue; - preference.setTitle(title.substring(0, delimeter)); - if (preference instanceof DialogPreference) - ((DialogPreference) preference).setDialogTitle(preference - .getTitle()); - preference - .setSummary(title.substring(delimeter + 1, title.length())); - } - } + private PreferenceSummaryHelper() { + } + + public static void updateSummary(PreferenceGroup group) { + for (int index = 0; index < group.getPreferenceCount(); index++) { + Preference preference = group.getPreference(index); + if (preference instanceof PreferenceGroup) { + updateSummary((PreferenceGroup) preference); + } + String titleAndSummary = preference.getTitle().toString(); + + if (!isTitleAndSummary(titleAndSummary)) { + continue; + } + + preference.setTitle(getPreferenceTitle(titleAndSummary)); + if (preference instanceof DialogPreference) { + ((DialogPreference) preference).setDialogTitle(preference.getTitle()); + } + preference.setSummary(getPreferenceSummary(titleAndSummary)); + } + } + + private static boolean isTitleAndSummary(String titleAndSummary) { + return titleAndSummary.contains("\n"); + } + + public static String getPreferenceTitle(String titleAndSummary) { + int delimiter = titleAndSummary.indexOf("\n"); + if (delimiter == -1) { + return titleAndSummary; + } + return titleAndSummary.substring(0, delimiter); + } + + private static String getPreferenceSummary(String titleAndSummary) { + int delimiter = titleAndSummary.indexOf("\n"); + if (delimiter == -1) { + return ""; + } + return titleAndSummary.substring(delimiter + 1, titleAndSummary.length()); + } } diff --git a/app/src/main/java/com/xabber/android/ui/helper/SingleActivity.java b/app/src/main/java/com/xabber/android/ui/helper/SingleActivity.java index 351ce25474..aba181c0d2 100644 --- a/app/src/main/java/com/xabber/android/ui/helper/SingleActivity.java +++ b/app/src/main/java/com/xabber/android/ui/helper/SingleActivity.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -22,31 +22,30 @@ /** * Single activity instance. - * + *

* Finish any previous instances. - * + * * @author alexander.ivanov - * */ public abstract class SingleActivity extends ManagedActivity { - private static Map, Activity> launched = new HashMap, Activity>(); + private static Map, Activity> launched = new HashMap, Activity>(); - @Override - protected void onCreate(Bundle savedInstanceState) { - Activity activity = launched.get(this.getClass()); - if (activity != null) - activity.finish(); - launched.put(this.getClass(), this); - super.onCreate(savedInstanceState); - } + @Override + protected void onCreate(Bundle savedInstanceState) { + Activity activity = launched.get(this.getClass()); + if (activity != null) + activity.finish(); + launched.put(this.getClass(), this); + super.onCreate(savedInstanceState); + } - @Override - protected void onDestroy() { - Activity activity = launched.get(this.getClass()); - if (activity == this) - launched.remove(this.getClass()); - super.onDestroy(); - } + @Override + protected void onDestroy() { + Activity activity = launched.get(this.getClass()); + if (activity == this) + launched.remove(this.getClass()); + super.onDestroy(); + } } diff --git a/app/src/main/java/com/xabber/android/ui/helper/StatusBarPainter.java b/app/src/main/java/com/xabber/android/ui/helper/StatusBarPainter.java new file mode 100644 index 0000000000..ea706603ee --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/helper/StatusBarPainter.java @@ -0,0 +1,38 @@ +package com.xabber.android.ui.helper; + +import android.os.Build; +import android.support.v4.app.FragmentActivity; +import android.view.Window; +import android.view.WindowManager; + + +public class StatusBarPainter { + + private final AccountPainter accountPainter; + private Window window; + + public StatusBarPainter(FragmentActivity activity) { + accountPainter = new AccountPainter(activity); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + window = activity.getWindow(); + window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + } + } + + public void updateWithAccountName(String account) { + updateWithColor(accountPainter.getAccountDarkColor(account)); + } + + + public void updateWithColor(int color) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + window.setStatusBarColor(color); + } + } + + public void updateWithDefaultColor() { + updateWithColor(accountPainter.getDefaultDarkColor()); + } +} diff --git a/app/src/main/java/com/xabber/android/ui/preferences/AccountEditor.java b/app/src/main/java/com/xabber/android/ui/preferences/AccountEditor.java new file mode 100644 index 0000000000..315b02e1f3 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/preferences/AccountEditor.java @@ -0,0 +1,157 @@ +/** + * Copyright (c) 2013, Redsolution LTD. All rights reserved. + * + * This file is part of Xabber project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License, Version 3. + * + * Xabber is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License, + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.xabber.android.ui.preferences; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceClickListener; +import android.support.v7.widget.Toolbar; + +import com.xabber.android.R; +import com.xabber.android.data.Application; +import com.xabber.android.data.account.AccountItem; +import com.xabber.android.data.account.AccountManager; +import com.xabber.android.data.intent.AccountIntentBuilder; +import com.xabber.android.ui.OAuthActivity; +import com.xabber.android.ui.dialog.OrbotInstallerDialogBuilder; +import com.xabber.android.ui.helper.BarPainter; +import com.xabber.android.ui.helper.ManagedActivity; + +public class AccountEditor extends ManagedActivity implements + OnPreferenceClickListener, AccountEditorFragment.AccountEditorFragmentInteractionListener { + + public static final String INVALIDATED_TOKEN = "com.xabber.android.ui.preferences.AccountEditor.INVALIDATED"; + private static final int OAUTH_WML_REQUEST_CODE = 1; + private static final String SAVED_TOKEN = "com.xabber.android.ui.preferences.AccountEditor.TOKEN"; + private String account; + private AccountItem accountItem; + + private String token; + private BarPainter barPainter; + + private static String getAccount(Intent intent) { + return AccountIntentBuilder.getAccount(intent); + } + + public static Intent createIntent(Context context, String account) { + return new AccountIntentBuilder(context, AccountEditor.class).setAccount(account).build(); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + account = AccountEditor.getAccount(getIntent()); + if (account == null) { + finish(); + return; + } + accountItem = AccountManager.getInstance().getAccount(account); + if (accountItem == null) { + Application.getInstance().onError(R.string.NO_SUCH_ACCOUNT); + finish(); + return; + } + + if (savedInstanceState == null) { + token = accountItem.getConnectionSettings().getPassword(); + + getFragmentManager().beginTransaction() + .add(R.id.fragment_container, new AccountEditorFragment()).commit(); + } else { + token = savedInstanceState.getString(SAVED_TOKEN); + } + + setContentView(R.layout.activity_with_toolbar_and_container); + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_default); + setSupportActionBar(toolbar); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + toolbar.setTitle(AccountManager.getInstance().getVerboseName(account)); + + barPainter = new BarPainter(this, toolbar); + barPainter.updateWithAccountName(account); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putString(SAVED_TOKEN, token); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == OAUTH_WML_REQUEST_CODE) { + if (resultCode == RESULT_OK) { + if (OAuthActivity.isInvalidated(data)) { + token = INVALIDATED_TOKEN; + } else { + String value = OAuthActivity.getToken(data); + if (value == null) { + Application.getInstance().onError(R.string.AUTHENTICATION_FAILED); + } else { + token = value; + } + } + + ((AccountEditorFragment) getFragmentManager().findFragmentById( + R.id.fragment_container)).onOAuthChange(); + } + } + } + + @Override + public boolean onPreferenceClick(Preference preference) { + if (getString(R.string.account_oauth_key).equals(preference.getKey())) { + startActivityForResult(OAuthActivity.createIntent(this, + accountItem.getConnectionSettings().getProtocol()), OAUTH_WML_REQUEST_CODE); + return true; + } + return false; + } + + @Override + public String getAccount() { + return account; + } + + @Override + public AccountItem getAccountItem() { + return accountItem; + } + + @Override + public String getToken() { + return token; + } + + @Override + public void onOAuthClick() { + startActivityForResult(OAuthActivity.createIntent(this, + accountItem.getConnectionSettings().getProtocol()), OAUTH_WML_REQUEST_CODE); + } + + @Override + public void showOrbotDialog() { + OrbotInstallerDialogBuilder.show(this); + } + + @Override + public void onColorChange(String colorName) { + barPainter.updateWithColorName(colorName); + } +} diff --git a/app/src/main/java/com/xabber/android/ui/preferences/AccountEditorFragment.java b/app/src/main/java/com/xabber/android/ui/preferences/AccountEditorFragment.java new file mode 100644 index 0000000000..23acfb1c31 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/preferences/AccountEditorFragment.java @@ -0,0 +1,247 @@ +package com.xabber.android.ui.preferences; + +import android.app.Activity; +import android.os.Bundle; +import android.preference.Preference; +import android.widget.Toast; + +import com.xabber.android.R; +import com.xabber.android.data.Application; +import com.xabber.android.data.account.AccountItem; +import com.xabber.android.data.account.AccountManager; +import com.xabber.android.data.account.AccountProtocol; +import com.xabber.android.data.account.ArchiveMode; +import com.xabber.android.data.connection.ProxyType; +import com.xabber.android.data.connection.TLSMode; +import com.xabber.android.ui.helper.OrbotHelper; + +import java.util.HashMap; +import java.util.Map; + +public class AccountEditorFragment extends BaseSettingsFragment + implements Preference.OnPreferenceClickListener { + + private AccountEditorFragmentInteractionListener mListener; + + private Preference oauthPreference; + + @Override + protected void onInflate(Bundle savedInstanceState) { + AccountProtocol protocol = mListener.getAccountItem().getConnectionSettings().getProtocol(); + if (protocol == AccountProtocol.xmpp) { + addPreferencesFromResource(R.xml.account_editor_xmpp); + } else if (protocol == AccountProtocol.gtalk) { + addPreferencesFromResource(R.xml.account_editor_xmpp); + } else if (protocol == AccountProtocol.wlm) { + addPreferencesFromResource(R.xml.account_editor_oauth); + } else { + throw new IllegalStateException(); + } + if (!Application.getInstance().isContactsSupported()) { + getPreferenceScreen().removePreference(findPreference(getString(R.string.account_syncable_key))); + } + + oauthPreference = findPreference(getString(R.string.account_oauth_key)); + if (oauthPreference != null) { + oauthPreference.setOnPreferenceClickListener(this); + } + onOAuthChange(); + AccountManager.getInstance().removeAuthorizationError(mListener.getAccount()); + } + + @Override + public void onPause() { + super.onPause(); + saveChanges(); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + String key = preference.getKey(); + + if (getString(R.string.account_port_key).equals(key)) { + try { + int newPort = Integer.parseInt((String) newValue); + // TODO: Not IPv6 Compatible + if (newPort < 0 || newPort > 0xFFFF) { + Toast.makeText(getActivity(), getString(R.string.account_invalid_port_range), + Toast.LENGTH_LONG).show(); + return false; + } + } catch (NumberFormatException e) { + Toast.makeText(getActivity(), getString(R.string.account_invalid_port), + Toast.LENGTH_LONG).show(); + return false; + } + } + + if (getString(R.string.account_proxy_port_key).equals(key)) { + try { + int newPort = Integer.parseInt((String) newValue); + // TODO: Not IPv6 Compatible + if (newPort < 0 || newPort > 0xFFFF) { + Toast.makeText(getActivity(), getString(R.string.account_proxy_invalid_port_range), + Toast.LENGTH_LONG).show(); + return false; + } + } catch (NumberFormatException e) { + Toast.makeText(getActivity(), getString(R.string.account_proxy_invalid_port), + Toast.LENGTH_LONG).show(); + return false; + } + } + + if (getString(R.string.account_tls_mode_key).equals(key) + || getString(R.string.account_archive_mode_key).equals(key) + || getString(R.string.account_proxy_type_key).equals(key) + || getString(R.string.account_color_key).equals(key)) { + preference.setSummary((String) newValue); + } else if (!getString(R.string.account_password_key).equals(key) + && !getString(R.string.account_proxy_password_key).equals(key) + && !getString(R.string.account_priority_key).equals(key)) { + super.onPreferenceChange(preference, newValue); + } + + if (getString(R.string.account_proxy_type_key).equals(key)) { + boolean enabled = !getString(R.string.account_proxy_type_none).equals(newValue) + && !getString(R.string.orbot).equals(newValue); + for (int id : new Integer[]{R.string.account_proxy_host_key, + R.string.account_proxy_port_key, R.string.account_proxy_user_key, + R.string.account_proxy_password_key,}) { + Preference proxyPreference = findPreference(getString(id)); + if (proxyPreference != null) { + proxyPreference.setEnabled(enabled); + } + } + } + + if (getString(R.string.account_color_key).equals(key)) { + mListener.onColorChange((String)newValue); + } + + return true; + } + + public void onOAuthChange() { + if (oauthPreference == null) { + return; + } + if (AccountEditor.INVALIDATED_TOKEN.equals(mListener.getToken())) { + oauthPreference.setSummary(R.string.account_oauth_invalidated); + } else { + oauthPreference.setSummary(R.string.account_oauth_summary); + } + } + + @Override + public boolean onPreferenceClick(Preference preference) { + if (getString(R.string.account_oauth_key).equals(preference.getKey())) { + mListener.onOAuthClick(); + return true; + } + return false; + } + + @Override + protected Map getValues() { + Map source = new HashMap<>(); + AccountItem accountItem = mListener.getAccountItem(); + + putValue(source, R.string.account_priority_key, accountItem.getPriority()); + putValue(source, R.string.account_enabled_key, accountItem.isEnabled()); + putValue(source, R.string.account_store_password_key, accountItem.isStorePassword()); + putValue(source, R.string.account_syncable_key, accountItem.isSyncable()); + putValue(source, R.string.account_archive_mode_key, accountItem.getArchiveMode().ordinal()); + putValue(source, R.string.account_color_key, accountItem.getColorIndex()); + + com.xabber.android.data.connection.ConnectionSettings connectionSettings = accountItem.getConnectionSettings(); + putValue(source, R.string.account_custom_key, connectionSettings.isCustom()); + putValue(source, R.string.account_host_key, connectionSettings.getHost()); + putValue(source, R.string.account_port_key, connectionSettings.getPort()); + putValue(source, R.string.account_server_key, connectionSettings.getServerName()); + putValue(source, R.string.account_username_key, connectionSettings.getUserName()); + putValue(source, R.string.account_password_key, connectionSettings.getPassword()); + putValue(source, R.string.account_resource_key, connectionSettings.getResource()); + putValue(source, R.string.account_sasl_key, connectionSettings.isSaslEnabled()); + putValue(source, R.string.account_tls_mode_key, connectionSettings.getTlsMode().ordinal()); + putValue(source, R.string.account_compression_key, connectionSettings.useCompression()); + putValue(source, R.string.account_proxy_type_key, connectionSettings.getProxyType().ordinal()); + putValue(source, R.string.account_proxy_host_key, connectionSettings.getProxyHost()); + putValue(source, R.string.account_proxy_port_key, connectionSettings.getProxyPort()); + putValue(source, R.string.account_proxy_user_key, connectionSettings.getProxyUser()); + putValue(source, R.string.account_proxy_password_key, connectionSettings.getProxyPassword()); + + return source; + } + + @Override + protected Map getPreferences(Map source) { + Map result = super.getPreferences(source); + if (oauthPreference != null) { + putValue(result, R.string.account_password_key, mListener.getAccount()); + } + return result; + } + + @Override + protected boolean setValues(Map source, Map result) { + ProxyType proxyType = ProxyType.values()[getInt(result, R.string.account_proxy_type_key)]; + if (proxyType == ProxyType.orbot && !OrbotHelper.isOrbotInstalled()) { + mListener.showOrbotDialog(); + return false; + } + AccountManager.getInstance().updateAccount( + mListener.getAccount(), + getBoolean(result, R.string.account_custom_key), + getString(result, R.string.account_host_key), + getInt(result, R.string.account_port_key), + getString(result, R.string.account_server_key), + getString(result, R.string.account_username_key), + getBoolean(result, R.string.account_store_password_key), + getString(result, R.string.account_password_key), + getString(result, R.string.account_resource_key), + getInt(result, R.string.account_priority_key), + getBoolean(result, R.string.account_enabled_key), + getBoolean(result, R.string.account_sasl_key), + TLSMode.values()[getInt(result, R.string.account_tls_mode_key)], + getBoolean(result, R.string.account_compression_key), + proxyType, + getString(result, R.string.account_proxy_host_key), + getInt(result, R.string.account_proxy_port_key), + getString(result, R.string.account_proxy_user_key), + getString(result, R.string.account_proxy_password_key), + getBoolean(result, R.string.account_syncable_key), + ArchiveMode.values()[getInt(result, R.string.account_archive_mode_key)], + getInt(result, R.string.account_color_key) + ); + + return true; + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + + try { + mListener = (AccountEditorFragmentInteractionListener) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity.toString() + + " must implement AccountEditorFragmentInteractionListener"); + } + } + + @Override + public void onDetach() { + super.onDetach(); + mListener = null; + } + + public interface AccountEditorFragmentInteractionListener { + String getAccount(); + AccountItem getAccountItem(); + String getToken(); + void onOAuthClick(); + void showOrbotDialog(); + void onColorChange(String colorName); + } +} diff --git a/app/src/main/java/com/xabber/android/ui/preferences/AccountList.java b/app/src/main/java/com/xabber/android/ui/preferences/AccountList.java new file mode 100644 index 0000000000..d01a8af4cf --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/preferences/AccountList.java @@ -0,0 +1,148 @@ +/** + * Copyright (c) 2013, Redsolution LTD. All rights reserved. + * + * This file is part of Xabber project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License, Version 3. + * + * Xabber is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License, + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.xabber.android.ui.preferences; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.widget.Toolbar; +import android.view.ContextMenu; +import android.view.MenuItem; + +import com.xabber.android.R; +import com.xabber.android.data.Application; +import com.xabber.android.data.account.AccountItem; +import com.xabber.android.data.account.AccountManager; +import com.xabber.android.data.account.OnAccountChangedListener; +import com.xabber.android.ui.AccountAdd; +import com.xabber.android.ui.StatusEditor; +import com.xabber.android.ui.adapter.AccountListAdapter; +import com.xabber.android.ui.adapter.BaseListEditorAdapter; +import com.xabber.android.ui.helper.PreferenceSummaryHelper; + +import java.util.Collection; + +public class AccountList extends BaseListEditor implements OnAccountChangedListener { + + private static final int CONTEXT_MENU_VIEW_ACCOUNT_ID = 0x20; + private static final int CONTEXT_MENU_STATUS_EDITOR_ID = 0x30; + + public static Intent createIntent(Context context) { + return new Intent(context, AccountList.class); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setSupportActionBar((Toolbar) findViewById(R.id.toolbar_default)); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + getSupportActionBar().setTitle(PreferenceSummaryHelper.getPreferenceTitle(getString(R.string.preference_accounts))); + } + + @Override + protected int getOptionsMenuId() { + return R.menu.add_account; + } + + @Override + protected int getAddActionId() { + return R.id.action_add_account; + } + + @Override + protected Intent getAddIntent() { + return AccountAdd.createIntent(this); + } + + @Override + protected Intent getEditIntent(String actionWith) { + return AccountEditor.createIntent(this, actionWith); + } + + @Override + protected int getRemoveTextResourceId() { + return R.string.account_delete; + } + + @Override + protected String getRemoveConfirmation(String actionWith) { + return getString(R.string.account_delete_confirm, AccountManager.getInstance().getVerboseName(actionWith)); + } + + @Override + protected void removeItem(String actionWith) { + AccountManager.getInstance().removeAccount(actionWith); + } + + @Override + protected BaseListEditorAdapter createListAdapter() { + return new AccountListAdapter(this); + } + + @Override + protected void onResume() { + super.onResume(); + Application.getInstance().addUIListener(OnAccountChangedListener.class, this); + } + + @Override + protected void onPause() { + super.onPause(); + Application.getInstance().removeUIListener(OnAccountChangedListener.class, this); + } + + @Override + protected void onCreateContextMenu(ContextMenu menu, String actionWith) { + final AccountItem accountItem = AccountManager.getInstance().getAccount(actionWith); + menu.setHeaderTitle(AccountManager.getInstance().getVerboseName(actionWith)); + if (accountItem.isEnabled()) { + menu.add(0, CONTEXT_MENU_STATUS_EDITOR_ID, 0, getResources() + .getText(R.string.status_editor)); + } + menu.add(0, CONTEXT_MENU_VIEW_ACCOUNT_ID, 0, getString(R.string.account_editor)); + super.onCreateContextMenu(menu, actionWith); + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + if (super.onContextItemSelected(item)) + return true; + if (item.getItemId() == CONTEXT_MENU_VIEW_ACCOUNT_ID) { + startActivity(getEditIntent(getActionWith())); + return true; + } else if (item.getItemId() == CONTEXT_MENU_STATUS_EDITOR_ID) { + startActivity(StatusEditor.createIntent(this, getActionWith())); + return true; + } + return false; + } + + @Override + public void onAccountsChanged(Collection accounts) { + adapter.onChange(); + } + + @Override + protected String getSavedValue(Bundle bundle, String key) { + return bundle.getString(key); + } + + @Override + protected void putSavedValue(Bundle bundle, String key, String actionWith) { + bundle.putString(key, actionWith); + } + +} diff --git a/app/src/main/java/com/xabber/android/ui/preferences/BaseListEditor.java b/app/src/main/java/com/xabber/android/ui/preferences/BaseListEditor.java new file mode 100644 index 0000000000..2d2fa56b18 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/preferences/BaseListEditor.java @@ -0,0 +1,203 @@ +/** + * Copyright (c) 2013, Redsolution LTD. All rights reserved. + * + * This file is part of Xabber project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License, Version 3. + * + * Xabber is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License, + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.xabber.android.ui.preferences; + +import android.app.Dialog; +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.widget.Toolbar; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.AdapterView; +import android.widget.AdapterView.AdapterContextMenuInfo; +import android.widget.ListView; + +import com.xabber.android.R; +import com.xabber.android.ui.adapter.BaseListEditorAdapter; +import com.xabber.android.ui.dialog.ConfirmDialogBuilder; +import com.xabber.android.ui.dialog.ConfirmDialogListener; +import com.xabber.android.ui.dialog.DialogBuilder; +import com.xabber.android.ui.helper.BarPainter; +import com.xabber.android.ui.helper.ManagedListActivity; + +/** + * Provide possibility to add, edit and delete list items. + * + * @param + * @author alexander.ivanov + */ +public abstract class BaseListEditor extends ManagedListActivity implements + AdapterView.OnItemClickListener, ConfirmDialogListener { + + private static final String SAVED_ACTION_WITH = "com.xabber.android.ui.BaseListActivity.SAVED_ACTION_WITH"; + + private static final int CONTEXT_MENU_DELETE_ID = 0x10; + private static final int DIALOG_DELETE_ID = 0x100; + protected BaseListEditorAdapter adapter; + private T actionWith; + private BarPainter barPainter; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (isFinishing()) + return; + onInflate(savedInstanceState); + if (savedInstanceState != null) + actionWith = getSavedValue(savedInstanceState, SAVED_ACTION_WITH); + else + actionWith = null; + ListView listView = getListView(); + listView.setOnItemClickListener(this); + registerForContextMenu(listView); + adapter = createListAdapter(); + setListAdapter(adapter); + } + + /** + * Inflates layout. + * + * @param savedInstanceState + */ + protected void onInflate(Bundle savedInstanceState) { + setContentView(R.layout.list); + + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_default); + + setSupportActionBar(toolbar); + + barPainter = new BarPainter(this, toolbar); + } + + protected abstract T getSavedValue(Bundle bundle, String key); + + protected abstract void putSavedValue(Bundle bundle, String key, + T actionWith); + + protected abstract int getOptionsMenuId(); + + protected abstract int getAddActionId(); + + protected abstract Intent getAddIntent(); + + protected abstract Intent getEditIntent(T actionWith); + + protected abstract int getRemoveTextResourceId(); + + protected abstract String getRemoveConfirmation(T actionWith); + + protected abstract void removeItem(T actionWith); + + protected abstract BaseListEditorAdapter createListAdapter(); + + protected T getActionWith() { + return actionWith; + } + + @Override + protected void onResume() { + super.onResume(); + adapter.onChange(); + barPainter.setDefaultColor(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + if (actionWith != null) + putSavedValue(outState, SAVED_ACTION_WITH, actionWith); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + getMenuInflater().inflate(getOptionsMenuId(), menu); + menu.findItem(getAddActionId()).setIntent(getAddIntent()); + return true; + } + + @SuppressWarnings("unchecked") + @Override + public void onCreateContextMenu(ContextMenu menu, View v, + ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + final AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; + actionWith = (T) getListView().getItemAtPosition(info.position); + if (actionWith == null) + // Add button + return; + onCreateContextMenu(menu, actionWith); + } + + protected void onCreateContextMenu(ContextMenu menu, T actionWith) { + menu.add(0, CONTEXT_MENU_DELETE_ID, 0, + getString(getRemoveTextResourceId())); + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + if (super.onContextItemSelected(item)) + return true; + if (item.getItemId() == CONTEXT_MENU_DELETE_ID) { + showDialog(DIALOG_DELETE_ID); + return true; + } + return false; + } + + @Override + protected Dialog onCreateDialog(int id) { + Dialog dialog = super.onCreateDialog(id); + if (dialog != null) + return dialog; + if (id == DIALOG_DELETE_ID) + return new ConfirmDialogBuilder(this, DIALOG_DELETE_ID, this) + .setMessage(getRemoveConfirmation(actionWith)).create(); + return null; + } + + @SuppressWarnings("unchecked") + @Override + public void onItemClick(AdapterView parent, View view, int position, + long id) { + T actionWith = (T) parent.getAdapter().getItem(position); + if (actionWith != null) { + Intent intent = getEditIntent(actionWith); + startActivity(intent); + } + } + + @Override + public void onAccept(DialogBuilder dialogBuilder) { + switch (dialogBuilder.getDialogId()) { + case DIALOG_DELETE_ID: + removeItem(actionWith); + adapter.onChange(); + break; + } + } + + @Override + public void onDecline(DialogBuilder dialogBuilder) { + } + + @Override + public void onCancel(DialogBuilder dialogBuilder) { + } + +} diff --git a/app/src/main/java/com/xabber/android/ui/preferences/BasePhrasePreferences.java b/app/src/main/java/com/xabber/android/ui/preferences/BasePhrasePreferences.java new file mode 100644 index 0000000000..259259f4bf --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/preferences/BasePhrasePreferences.java @@ -0,0 +1,46 @@ +package com.xabber.android.ui.preferences; + + +import android.os.Bundle; +import android.support.v7.widget.Toolbar; + +import com.xabber.android.R; +import com.xabber.android.data.message.phrase.Phrase; +import com.xabber.android.ui.helper.BarPainter; +import com.xabber.android.ui.helper.ManagedActivity; + +public abstract class BasePhrasePreferences extends ManagedActivity + implements PhraseEditorFragment.OnPhraseEditorFragmentInteractionListener { + + private Phrase phrase; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_with_toolbar_and_container); + + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_default); + + setSupportActionBar(toolbar); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + BarPainter barPainter = new BarPainter(this, toolbar); + barPainter.setDefaultColor(); + + if (savedInstanceState == null) { + getFragmentManager().beginTransaction() + .add(R.id.fragment_container, new PhraseEditorFragment()).commit(); + } + } + + @Override + public Phrase getPhrase() { + return phrase; + } + + @Override + public void setPhrase(Phrase phrase) { + this.phrase = phrase; + } +} diff --git a/app/src/main/java/com/xabber/android/ui/preferences/BaseSettingsFragment.java b/app/src/main/java/com/xabber/android/ui/preferences/BaseSettingsFragment.java new file mode 100644 index 0000000000..3d60bd6468 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/preferences/BaseSettingsFragment.java @@ -0,0 +1,241 @@ +package com.xabber.android.ui.preferences; + + +import android.net.Uri; +import android.os.Bundle; +import android.preference.CheckBoxPreference; +import android.preference.EditTextPreference; +import android.preference.ListPreference; +import android.preference.Preference; +import android.preference.PreferenceFragment; +import android.preference.PreferenceScreen; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.xabber.android.ui.helper.PreferenceSummaryHelper; +import com.xabber.android.ui.widget.RingtonePreference; + +import java.util.HashMap; +import java.util.Map; + +public abstract class BaseSettingsFragment extends PreferenceFragment + implements Preference.OnPreferenceChangeListener { + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = super.onCreateView(inflater, container, savedInstanceState); + + onInflate(savedInstanceState); + + PreferenceSummaryHelper.updateSummary(getPreferenceScreen()); + if (savedInstanceState == null) + operation(Operation.read); + PreferenceScreen preferenceScreen = getPreferenceScreen(); + for (int index = 0; index < preferenceScreen.getPreferenceCount(); index++) { + Preference preference = preferenceScreen.getPreference(index); + preference.setOnPreferenceChangeListener(this); + if (preference instanceof EditTextPreference) + onPreferenceChange(preference, + ((EditTextPreference) preference).getText()); + else if (preference instanceof CheckBoxPreference) + onPreferenceChange(preference, + ((CheckBoxPreference) preference).isChecked()); + else if (preference instanceof ListPreference) + onPreferenceChange(preference, + ((ListPreference) preference).getValue()); + } + + return view; + } + + /** + * Inflates layout. + * + * @param savedInstanceState + */ + protected abstract void onInflate(Bundle savedInstanceState); + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + if (preference instanceof EditTextPreference) + preference.setSummary((String) newValue); + return true; + } + + /** + * Possible operations. + */ + private enum Operation { + save, discard, read + } + + protected void putValue(Map map, int resoureId, Object value) { + map.put(getString(resoureId), value); + } + + protected String getString(Map map, int resoureId) { + return (String) map.get(getString(resoureId)); + } + + protected int getInt(Map map, int resoureId) { + return (Integer) map.get(getString(resoureId)); + } + + protected boolean getBoolean(Map map, int resoureId) { + return (Boolean) map.get(getString(resoureId)); + } + + protected Uri getUri(Map map, int resoureId) { + return (Uri) map.get(getString(resoureId)); + } + + /** + * @param intent + * @return Whether an operation succeed. + */ + private boolean operation(Operation selected) { + Map source = getValues(); + if (selected == Operation.read) + setPreferences(source); + else { + Map result = getPreferences(source); + for (Map.Entry entry : source.entrySet()) + if (!result.containsKey(entry.getKey())) + result.put(entry.getKey(), entry.getValue()); + if (selected == Operation.save) + return setValues(source, result); + else if (selected == Operation.discard) { + for (String key : source.keySet()) + if (hasChanges(source, result, key)) + return false; + } else + throw new IllegalStateException(); + } + return true; + } + + /** + * @param source + * @param result + * @param key + * @return Whether value has been changed. + */ + protected boolean hasChanges(Map source, + Map result, String key) { + Object sourceValue = source.get(key); + Object targetValue = result.get(key); + return (sourceValue == null && targetValue != null) + || (sourceValue != null && !sourceValue.equals(targetValue)); + } + + /** + * @param source + * @param result + * @param resourceId + * @return Whether value has been changed. + */ + protected boolean hasChanges(Map source, + Map result, int resourceId) { + return hasChanges(source, result, getString(resourceId)); + } + + /** + * @return Map with source values. + */ + protected abstract Map getValues(); + + /** + * Set values to the UI elements. + * + * @param source + */ + protected void setPreferences(Map source) { + PreferenceScreen preferenceScreen = getPreferenceScreen(); + for (int index = 0; index < preferenceScreen.getPreferenceCount(); index++) { + Preference preference = preferenceScreen.getPreference(index); + Object value = source.get(preference.getKey()); + setPreference(preference, value); + } + } + + /** + * Set value to the UI element. + * + * @param preference + * @param value + */ + protected void setPreference(Preference preference, Object value) { + if (preference instanceof EditTextPreference) + ((EditTextPreference) preference) + .setText(value instanceof Integer ? String.valueOf(value) + : (String) value); + else if (preference instanceof CheckBoxPreference) + ((CheckBoxPreference) preference).setChecked((Boolean) value); + else if (preference instanceof ListPreference) + ((ListPreference) preference).setValueIndex((Integer) value); + else if (preference instanceof RingtonePreference) + ((RingtonePreference) preference).setUri((Uri) value); + } + + /** + * Get values from the UI elements. + * + * @param source + * @return + */ + protected Map getPreferences(Map source) { + Map result = new HashMap<>(); + PreferenceScreen preferenceScreen = getPreferenceScreen(); + for (int index = 0; index < preferenceScreen.getPreferenceCount(); index++) { + Preference preference = preferenceScreen.getPreference(index); + result.put(preference.getKey(), getPreference(preference, source)); + } + return result; + } + + /** + * Get value from the UI element. + * + * @param preference + * @param source + * @return + */ + protected Object getPreference(Preference preference, Map source) { + if (preference instanceof PreferenceScreen) { + return null; + } else if (preference instanceof EditTextPreference) { + String value = ((EditTextPreference) preference).getText(); + if (source.get(preference.getKey()) instanceof Integer) + try { + return Integer.parseInt(value); + } catch (Exception NumberFormatException) { + return null; + } + else + return value; + } else if (preference instanceof CheckBoxPreference) + return ((CheckBoxPreference) preference).isChecked(); + else if (preference instanceof ListPreference) + return ((ListPreference) preference) + .findIndexOfValue(((ListPreference) preference).getValue()); + else if (preference instanceof RingtonePreference) + return ((RingtonePreference) preference).getUri(); + throw new IllegalStateException(); + } + + /** + * Apply result values. + * + * @param source + * @param result + * @return Whether operation succeed. + */ + protected abstract boolean setValues(Map source, Map result); + + + protected boolean saveChanges() { + return operation(Operation.save); + } + +} diff --git a/app/src/main/java/com/xabber/android/ui/preferences/CacheClearDialog.java b/app/src/main/java/com/xabber/android/ui/preferences/CacheClearDialog.java new file mode 100644 index 0000000000..030fbc98b9 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/preferences/CacheClearDialog.java @@ -0,0 +1,34 @@ +package com.xabber.android.ui.preferences; + +import android.app.ProgressDialog; +import android.content.Context; +import android.preference.DialogPreference; +import android.util.AttributeSet; + +import com.xabber.android.R; +import com.xabber.android.data.Application; +import com.xabber.android.data.account.AccountManager; +import com.xabber.android.data.account.StatusMode; + +public class CacheClearDialog extends DialogPreference { + public CacheClearDialog(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onDialogClosed(boolean positiveResult) { + super.onDialogClosed(positiveResult); + + if (positiveResult) { + AccountManager.getInstance().setStatus(StatusMode.unavailable, null); + Application.getInstance().requestToClear(); + Application.getInstance().requestToClose(); + + ProgressDialog progressDialog = new ProgressDialog(getContext()); + progressDialog.setMessage(getContext().getString(R.string.application_state_closing)); + progressDialog.setCancelable(false); + progressDialog.setIndeterminate(true); + progressDialog.show(); + } + } +} diff --git a/app/src/main/java/com/xabber/android/ui/preferences/ChatContactSettings.java b/app/src/main/java/com/xabber/android/ui/preferences/ChatContactSettings.java new file mode 100644 index 0000000000..1cc00e0f47 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/preferences/ChatContactSettings.java @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2013, Redsolution LTD. All rights reserved. + * + * This file is part of Xabber project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License, Version 3. + * + * Xabber is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License, + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.xabber.android.ui.preferences; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.widget.Toolbar; + +import com.xabber.android.R; +import com.xabber.android.data.Application; +import com.xabber.android.data.account.AccountItem; +import com.xabber.android.data.account.AccountManager; +import com.xabber.android.data.intent.EntityIntentBuilder; +import com.xabber.android.ui.helper.BarPainter; +import com.xabber.android.ui.helper.ManagedActivity; + +public class ChatContactSettings extends ManagedActivity + implements ChatContactSettingsFragment.ChatEditorFragmentInteractionListener { + + private String account; + private String user; + private AccountItem accountItem; + + public static Intent createIntent(Context context, String account, String user) { + return new EntityIntentBuilder(context, ChatContactSettings.class).setAccount(account).setUser(user).build(); + } + + private static String getAccount(Intent intent) { + return EntityIntentBuilder.getAccount(intent); + } + + private static String getUser(Intent intent) { + return EntityIntentBuilder.getUser(intent); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + account = getAccount(getIntent()); + user = getUser(getIntent()); + accountItem = AccountManager.getInstance().getAccount(account); + + if (accountItem == null || user == null) { + Application.getInstance().onError(R.string.ENTRY_IS_NOT_FOUND); + finish(); + return; + } + + setContentView(R.layout.activity_with_toolbar_and_container); + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_default); + setSupportActionBar(toolbar); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + BarPainter barPainter = new BarPainter(this, toolbar); + barPainter.updateWithAccountName(account); + + if (savedInstanceState == null) { + getFragmentManager().beginTransaction() + .add(R.id.fragment_container, new ChatContactSettingsFragment()).commit(); + } + } + + @Override + public String getAccount() { + return account; + } + + @Override + public AccountItem getAccountItem() { + return accountItem; + } + + @Override + public String getUser() { + return user; + } +} diff --git a/app/src/main/java/com/xabber/android/ui/preferences/ChatContactSettingsFragment.java b/app/src/main/java/com/xabber/android/ui/preferences/ChatContactSettingsFragment.java new file mode 100644 index 0000000000..eef45c664b --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/preferences/ChatContactSettingsFragment.java @@ -0,0 +1,115 @@ +package com.xabber.android.ui.preferences; + + +import android.app.Activity; +import android.os.Bundle; + +import com.xabber.android.R; +import com.xabber.android.data.account.AccountItem; +import com.xabber.android.data.account.ArchiveMode; +import com.xabber.android.data.message.chat.ChatManager; +import com.xabber.android.data.message.chat.ShowMessageTextInNotification; + +import java.util.HashMap; +import java.util.Map; + +public class ChatContactSettingsFragment extends BaseSettingsFragment { + + private ChatEditorFragmentInteractionListener mListener; + + @Override + protected void onInflate(Bundle savedInstanceState) { + addPreferencesFromResource(R.xml.preference_chat_contact); + + AccountItem accountItem = mListener.getAccountItem(); + + if (accountItem.getArchiveMode() == ArchiveMode.server + || accountItem.getArchiveMode() == ArchiveMode.dontStore) { + getPreferenceScreen().removePreference(getPreferenceScreen() + .findPreference(getString(R.string.chat_save_history_key))); + } + } + + @Override + public void onPause() { + super.onPause(); + saveChanges(); + } + + @Override + protected Map getValues() { + Map map = new HashMap<>(); + String account = mListener.getAccount(); + String user = mListener.getUser(); + + putValue(map, R.string.chat_save_history_key, ChatManager.getInstance() + .isSaveMessages(account, user)); + putValue(map, R.string.chat_events_visible_chat_key, ChatManager + .getInstance().isNotifyVisible(account, user)); + putValue(map, R.string.chat_events_show_text_key, ChatManager + .getInstance().getShowText(account, user).ordinal()); + + putValue(map, R.string.chat_events_vibro_key, ChatManager.getInstance() + .isMakeVibro(account, user)); + putValue(map, R.string.chat_events_sound_key, ChatManager.getInstance() + .getSound(account, user)); + putValue(map, R.string.chat_events_suppress_100_key, ChatManager.getInstance() + .isSuppress100(account, user)); + return map; + } + + @Override + protected boolean setValues(Map source, Map result) { + String account = mListener.getAccount(); + String user = mListener.getUser(); + + if (hasChanges(source, result, R.string.chat_save_history_key)) + ChatManager.getInstance().setSaveMessages(account, user, + getBoolean(result, R.string.chat_save_history_key)); + + if (hasChanges(source, result, R.string.chat_events_visible_chat_key)) + ChatManager.getInstance().setNotifyVisible(account, user, + getBoolean(result, R.string.chat_events_visible_chat_key)); + + if (hasChanges(source, result, R.string.chat_events_show_text_key)) { + ChatManager.getInstance().setShowText(account, user, + ShowMessageTextInNotification.fromInteger(getInt(result, R.string.chat_events_show_text_key))); + } + + if (hasChanges(source, result, R.string.chat_events_vibro_key)) + ChatManager.getInstance().setMakeVibro(account, user, + getBoolean(result, R.string.chat_events_vibro_key)); + + if (hasChanges(source, result, R.string.chat_events_sound_key)) + ChatManager.getInstance().setSound(account, user, + getUri(result, R.string.chat_events_sound_key)); + + if (hasChanges(source, result, R.string.chat_events_suppress_100_key)) + ChatManager.getInstance().setSuppress100(account, user, + getBoolean(result, R.string.chat_events_suppress_100_key)); + return true; + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + try { + mListener = (ChatEditorFragmentInteractionListener) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity.toString() + + " must implement ChatEditorFragmentInteractionListener"); + } + } + + @Override + public void onDetach() { + super.onDetach(); + mListener = null; + } + + public interface ChatEditorFragmentInteractionListener { + String getAccount(); + AccountItem getAccountItem(); + String getUser(); + } +} diff --git a/app/src/main/java/com/xabber/android/ui/preferences/ChatGlobalSettings.java b/app/src/main/java/com/xabber/android/ui/preferences/ChatGlobalSettings.java new file mode 100644 index 0000000000..68ce298a8c --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/preferences/ChatGlobalSettings.java @@ -0,0 +1,37 @@ +package com.xabber.android.ui.preferences; + +import android.os.Bundle; +import android.support.v7.widget.Toolbar; + +import com.xabber.android.R; +import com.xabber.android.ui.helper.BarPainter; +import com.xabber.android.ui.helper.ManagedActivity; +import com.xabber.android.ui.helper.PreferenceSummaryHelper; + +public class ChatGlobalSettings extends ManagedActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (isFinishing()) + return; + + setContentView(R.layout.activity_with_toolbar_and_container); + + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_default); + + setSupportActionBar(toolbar); + + BarPainter barPainter = new BarPainter(this, toolbar); + barPainter.setDefaultColor(); + + setTitle(PreferenceSummaryHelper.getPreferenceTitle(getString(R.string.chat_viewer))); + + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + if (savedInstanceState == null) { + getFragmentManager().beginTransaction() + .add(R.id.fragment_container, new ChatGlobalSettingsFragment()).commit(); + } + } +} diff --git a/app/src/main/java/com/xabber/android/ui/preferences/ChatGlobalSettingsFragment.java b/app/src/main/java/com/xabber/android/ui/preferences/ChatGlobalSettingsFragment.java new file mode 100644 index 0000000000..b4f4128a68 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/preferences/ChatGlobalSettingsFragment.java @@ -0,0 +1,17 @@ +package com.xabber.android.ui.preferences; + +import android.os.Bundle; + +import com.xabber.android.R; +import com.xabber.android.ui.helper.PreferenceSummaryHelper; + +public class ChatGlobalSettingsFragment extends android.preference.PreferenceFragment { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + addPreferencesFromResource(R.xml.preference_chat_global); + + PreferenceSummaryHelper.updateSummary(getPreferenceScreen()); + } +} diff --git a/app/src/main/java/com/xabber/android/ui/preferences/ConnectionSettings.java b/app/src/main/java/com/xabber/android/ui/preferences/ConnectionSettings.java new file mode 100644 index 0000000000..d2d0589507 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/preferences/ConnectionSettings.java @@ -0,0 +1,37 @@ +package com.xabber.android.ui.preferences; + +import android.os.Bundle; +import android.support.v7.widget.Toolbar; + +import com.xabber.android.R; +import com.xabber.android.ui.helper.BarPainter; +import com.xabber.android.ui.helper.ManagedActivity; +import com.xabber.android.ui.helper.PreferenceSummaryHelper; + +public class ConnectionSettings extends ManagedActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (isFinishing()) + return; + + setContentView(R.layout.activity_with_toolbar_and_container); + + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_default); + + setSupportActionBar(toolbar); + + BarPainter barPainter = new BarPainter(this, toolbar); + barPainter.setDefaultColor(); + + setTitle(PreferenceSummaryHelper.getPreferenceTitle(getString(R.string.preference_connection))); + + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + if (savedInstanceState == null) { + getFragmentManager().beginTransaction() + .add(R.id.fragment_container, new ConnectionSettingsFragment()).commit(); + } + } +} diff --git a/app/src/main/java/com/xabber/android/ui/preferences/ConnectionSettingsFragment.java b/app/src/main/java/com/xabber/android/ui/preferences/ConnectionSettingsFragment.java new file mode 100644 index 0000000000..aa6cea6a81 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/preferences/ConnectionSettingsFragment.java @@ -0,0 +1,17 @@ +package com.xabber.android.ui.preferences; + +import android.os.Bundle; + +import com.xabber.android.R; +import com.xabber.android.ui.helper.PreferenceSummaryHelper; + +public class ConnectionSettingsFragment extends android.preference.PreferenceFragment { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + addPreferencesFromResource(R.xml.preference_connection); + + PreferenceSummaryHelper.updateSummary(getPreferenceScreen()); + } +} diff --git a/app/src/main/java/com/xabber/android/ui/preferences/ContactListSettings.java b/app/src/main/java/com/xabber/android/ui/preferences/ContactListSettings.java new file mode 100644 index 0000000000..98cd22bc61 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/preferences/ContactListSettings.java @@ -0,0 +1,36 @@ +package com.xabber.android.ui.preferences; + +import android.os.Bundle; +import android.support.v7.widget.Toolbar; + +import com.xabber.android.R; +import com.xabber.android.ui.helper.BarPainter; +import com.xabber.android.ui.helper.ManagedActivity; +import com.xabber.android.ui.helper.PreferenceSummaryHelper; + +public class ContactListSettings extends ManagedActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (isFinishing()) + return; + + setContentView(R.layout.activity_with_toolbar_and_container); + + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_default); + + setSupportActionBar(toolbar); + + BarPainter barPainter = new BarPainter(this, toolbar); + barPainter.setDefaultColor(); + + setTitle(PreferenceSummaryHelper.getPreferenceTitle(getString(R.string.preference_contacts))); + + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + if (savedInstanceState == null) { + getFragmentManager().beginTransaction() + .add(R.id.fragment_container, new ContactListSettingsFragment()).commit(); + } + } +} diff --git a/app/src/main/java/com/xabber/android/ui/preferences/ContactListSettingsFragment.java b/app/src/main/java/com/xabber/android/ui/preferences/ContactListSettingsFragment.java new file mode 100644 index 0000000000..6a7fbc724a --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/preferences/ContactListSettingsFragment.java @@ -0,0 +1,56 @@ +package com.xabber.android.ui.preferences; + +import android.content.SharedPreferences; +import android.os.Bundle; +import android.preference.CheckBoxPreference; +import android.preference.PreferenceFragment; +import android.preference.PreferenceManager; + +import com.xabber.android.R; +import com.xabber.android.data.SettingsManager; +import com.xabber.android.ui.helper.PreferenceSummaryHelper; + +public class ContactListSettingsFragment extends PreferenceFragment + implements SharedPreferences.OnSharedPreferenceChangeListener { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + addPreferencesFromResource(R.xml.preferences_contact_list); + + PreferenceSummaryHelper.updateSummary(getPreferenceScreen()); + } + + @Override + public void onResume() { + super.onResume(); + PreferenceManager.getDefaultSharedPreferences(getActivity()) + .registerOnSharedPreferenceChangeListener(this); + } + + @Override + public void onPause() { + super.onPause(); + PreferenceManager.getDefaultSharedPreferences(getActivity()) + .unregisterOnSharedPreferenceChangeListener(this); + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + if (key.equals(getString(R.string.contacts_show_accounts_key)) + || key.equals(getString(R.string.contacts_show_groups_key))) { + changeGrouping(); + } + } + + private void changeGrouping() { + boolean grouped = SettingsManager.contactsShowAccounts() + || SettingsManager.contactsShowGroups(); + ((CheckBoxPreference) getPreferenceScreen().findPreference( + getString(R.string.contacts_stay_active_chats_key))) + .setChecked(grouped); + getPreferenceScreen().findPreference(getString(R.string.contacts_show_empty_groups_key)) + .setEnabled(grouped); + } + +} diff --git a/app/src/main/java/com/xabber/android/ui/preferences/ContactResetOfflineSettingsDialog.java b/app/src/main/java/com/xabber/android/ui/preferences/ContactResetOfflineSettingsDialog.java new file mode 100644 index 0000000000..ea60aab01d --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/preferences/ContactResetOfflineSettingsDialog.java @@ -0,0 +1,22 @@ +package com.xabber.android.ui.preferences; + +import android.content.Context; +import android.preference.DialogPreference; +import android.util.AttributeSet; + +import com.xabber.android.data.roster.GroupManager; + +public class ContactResetOfflineSettingsDialog extends DialogPreference { + public ContactResetOfflineSettingsDialog(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onDialogClosed(boolean positiveResult) { + super.onDialogClosed(positiveResult); + + if (positiveResult) { + GroupManager.getInstance().resetShowOfflineModes(); + } + } +} diff --git a/app/src/main/java/com/xabber/android/ui/preferences/DebugSettings.java b/app/src/main/java/com/xabber/android/ui/preferences/DebugSettings.java new file mode 100644 index 0000000000..6ced3a20ea --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/preferences/DebugSettings.java @@ -0,0 +1,37 @@ +package com.xabber.android.ui.preferences; + +import android.os.Bundle; +import android.support.v7.widget.Toolbar; + +import com.xabber.android.R; +import com.xabber.android.ui.helper.BarPainter; +import com.xabber.android.ui.helper.ManagedActivity; +import com.xabber.android.ui.helper.PreferenceSummaryHelper; + +public class DebugSettings extends ManagedActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (isFinishing()) + return; + + setContentView(R.layout.activity_with_toolbar_and_container); + + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_default); + + setSupportActionBar(toolbar); + + BarPainter barPainter = new BarPainter(this, toolbar); + barPainter.setDefaultColor(); + + setTitle(PreferenceSummaryHelper.getPreferenceTitle(getString(R.string.preference_debug))); + + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + if (savedInstanceState == null) { + getFragmentManager().beginTransaction() + .add(R.id.fragment_container, new DebugSettingsFragment()).commit(); + } + } +} diff --git a/app/src/main/java/com/xabber/android/ui/preferences/DebugSettingsFragment.java b/app/src/main/java/com/xabber/android/ui/preferences/DebugSettingsFragment.java new file mode 100644 index 0000000000..b63fa4e715 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/preferences/DebugSettingsFragment.java @@ -0,0 +1,22 @@ +package com.xabber.android.ui.preferences; + +import android.os.Bundle; + +import com.xabber.android.R; +import com.xabber.android.data.LogManager; +import com.xabber.android.ui.helper.PreferenceSummaryHelper; + +public class DebugSettingsFragment extends android.preference.PreferenceFragment { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + addPreferencesFromResource(R.xml.preference_debug); + + if (!LogManager.isDebugable()) { + getPreferenceScreen().removePreference(getPreferenceScreen().findPreference(getString(R.string.debug_log_key))); + } + + PreferenceSummaryHelper.updateSummary(getPreferenceScreen()); + } +} diff --git a/app/src/main/java/com/xabber/android/ui/preferences/NotificationsSettings.java b/app/src/main/java/com/xabber/android/ui/preferences/NotificationsSettings.java new file mode 100644 index 0000000000..e781844b01 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/preferences/NotificationsSettings.java @@ -0,0 +1,38 @@ +package com.xabber.android.ui.preferences; + + +import android.os.Bundle; +import android.support.v7.widget.Toolbar; + +import com.xabber.android.R; +import com.xabber.android.ui.helper.BarPainter; +import com.xabber.android.ui.helper.ManagedActivity; +import com.xabber.android.ui.helper.PreferenceSummaryHelper; + +public class NotificationsSettings extends ManagedActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (isFinishing()) + return; + + setContentView(R.layout.activity_with_toolbar_and_container); + + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_default); + + setSupportActionBar(toolbar); + + BarPainter barPainter = new BarPainter(this, toolbar); + barPainter.setDefaultColor(); + + setTitle(PreferenceSummaryHelper.getPreferenceTitle(getString(R.string.preference_events))); + + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + if (savedInstanceState == null) { + getFragmentManager().beginTransaction() + .add(R.id.fragment_container, new NotificationsSettingsFragment()).commit(); + } + } +} diff --git a/app/src/main/java/com/xabber/android/ui/preferences/NotificationsSettingsFragment.java b/app/src/main/java/com/xabber/android/ui/preferences/NotificationsSettingsFragment.java new file mode 100644 index 0000000000..37d49a04bc --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/preferences/NotificationsSettingsFragment.java @@ -0,0 +1,18 @@ +package com.xabber.android.ui.preferences; + + +import android.os.Bundle; + +import com.xabber.android.R; +import com.xabber.android.ui.helper.PreferenceSummaryHelper; + +public class NotificationsSettingsFragment extends android.preference.PreferenceFragment { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + addPreferencesFromResource(R.xml.preference_notifications); + + PreferenceSummaryHelper.updateSummary(getPreferenceScreen()); + } +} diff --git a/app/src/main/java/com/xabber/android/ui/preferences/PhraseAdder.java b/app/src/main/java/com/xabber/android/ui/preferences/PhraseAdder.java new file mode 100644 index 0000000000..987cdcb868 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/preferences/PhraseAdder.java @@ -0,0 +1,52 @@ +package com.xabber.android.ui.preferences; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; + +import com.xabber.android.R; +import com.xabber.android.data.intent.SegmentIntentBuilder; + +public class PhraseAdder extends BasePhrasePreferences { + + public static Intent createIntent(Context context) { + return new SegmentIntentBuilder<>(context, PhraseAdder.class).build(); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setTitle(R.string.phrase_add); + getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_clear_white_24dp); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + getMenuInflater().inflate(R.menu.save, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_save: + + boolean success = ((PhraseEditorFragment) getFragmentManager() + .findFragmentById(R.id.fragment_container)).saveChanges(); + + if (success) { + finish(); + } + + return true; + default: + return super.onOptionsItemSelected(item); + } + + } + +} diff --git a/app/src/main/java/com/xabber/android/ui/preferences/PhraseEditor.java b/app/src/main/java/com/xabber/android/ui/preferences/PhraseEditor.java new file mode 100644 index 0000000000..b55259270c --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/preferences/PhraseEditor.java @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2013, Redsolution LTD. All rights reserved. + * + * This file is part of Xabber project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License, Version 3. + * + * Xabber is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License, + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.xabber.android.ui.preferences; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; + +import com.xabber.android.R; +import com.xabber.android.data.Application; +import com.xabber.android.data.intent.SegmentIntentBuilder; +import com.xabber.android.data.message.phrase.Phrase; +import com.xabber.android.data.message.phrase.PhraseManager; + +public class PhraseEditor extends BasePhrasePreferences { + + public static Intent createIntent(Context context, Integer phraseIndex) { + SegmentIntentBuilder builder = new SegmentIntentBuilder<>( + context, PhraseEditor.class); + if (phraseIndex != null) + builder.addSegment(phraseIndex.toString()); + return builder.build(); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + + Integer index = getPhraseIndex(getIntent()); + if (index == null) { + finish(); + return; + } + + Phrase phrase = PhraseManager.getInstance().getPhrase(index); + if (phrase == null) { + finish(); + return; + } + setPhrase(phrase); + + String title = phrase.getText(); + if ("".equals(title)) + title = Application.getInstance().getString( + R.string.phrase_empty); + setTitle(title); + + } + + @Override + protected void onPause() { + super.onPause(); + + ((PhraseEditorFragment) getFragmentManager() + .findFragmentById(R.id.fragment_container)).saveChanges(); + } + + private Integer getPhraseIndex(Intent intent) { + String value = SegmentIntentBuilder.getSegment(intent, 0); + if (value == null) + return null; + else + return Integer.valueOf(value); + } +} diff --git a/app/src/main/java/com/xabber/android/ui/preferences/PhraseEditorFragment.java b/app/src/main/java/com/xabber/android/ui/preferences/PhraseEditorFragment.java new file mode 100644 index 0000000000..ee1aa17704 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/preferences/PhraseEditorFragment.java @@ -0,0 +1,104 @@ +package com.xabber.android.ui.preferences; + +import android.app.Activity; +import android.net.Uri; +import android.os.Bundle; +import android.provider.Settings; +import android.util.Log; +import android.widget.Toast; + +import com.xabber.android.R; +import com.xabber.android.data.message.phrase.Phrase; +import com.xabber.android.data.message.phrase.PhraseManager; + +import java.util.HashMap; +import java.util.Map; +import java.util.regex.PatternSyntaxException; + +public class PhraseEditorFragment extends BaseSettingsFragment { + + private OnPhraseEditorFragmentInteractionListener mListener; + + @Override + protected void onInflate(Bundle savedInstanceState) { + addPreferencesFromResource(R.xml.phrase_editor); + } + + @Override + protected Map getValues() { + Phrase phrase = mListener.getPhrase(); + Map source = new HashMap<>(); + + putValue(source, R.string.phrase_text_key, + phrase == null ? "" : phrase.getText()); + putValue(source, R.string.phrase_user_key, + phrase == null ? "" : phrase.getUser()); + putValue(source, R.string.phrase_group_key, phrase == null ? "" + : phrase.getGroup()); + putValue(source, R.string.phrase_regexp_key, phrase != null && phrase.isRegexp()); + putValue(source, R.string.phrase_sound_key, + phrase == null ? Settings.System.DEFAULT_NOTIFICATION_URI + : phrase.getSound()); + return source; + } + + @Override + protected boolean setValues(Map source, + Map result) { + + + String text = getString(result, R.string.phrase_text_key); + String user = getString(result, R.string.phrase_user_key); + String group = getString(result, R.string.phrase_group_key); + boolean regexp = getBoolean(result, R.string.phrase_regexp_key); + Uri sound = getUri(result, R.string.phrase_sound_key); + + Log.i("PhraseEditorFragment", "setValues. text: " + text); + + if (regexp) { + try { + Phrase.compile(text); + Phrase.compile(user); + Phrase.compile(group); + } catch (PatternSyntaxException e) { + Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_LONG).show(); + return false; + } + } + + Phrase phrase = mListener.getPhrase(); + + if (phrase == null && "".equals(text) && "".equals(user) && "".equals(group)) { + return true; + } + Log.i("PhraseEditorFragment", "updateOrCreatePhrase"); + PhraseManager.getInstance().updateOrCreatePhrase(phrase, text, user, group, regexp, sound); + + mListener.setPhrase(phrase); + + return true; + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + try { + mListener = (OnPhraseEditorFragmentInteractionListener) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity.toString() + + " must implement OnThemeSettingsFragmentInteractionListener"); + } + } + + @Override + public void onDetach() { + super.onDetach(); + mListener = null; + } + + public interface OnPhraseEditorFragmentInteractionListener { + Phrase getPhrase(); + + void setPhrase(Phrase phrase); + } +} diff --git a/app/src/main/java/com/xabber/android/ui/preferences/PhraseList.java b/app/src/main/java/com/xabber/android/ui/preferences/PhraseList.java new file mode 100644 index 0000000000..1db1dd007f --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/preferences/PhraseList.java @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2013, Redsolution LTD. All rights reserved. + * + * This file is part of Xabber project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License, Version 3. + * + * Xabber is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License, + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.xabber.android.ui.preferences; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; + +import com.xabber.android.R; +import com.xabber.android.data.Application; +import com.xabber.android.data.message.phrase.PhraseManager; +import com.xabber.android.ui.adapter.BaseListEditorAdapter; +import com.xabber.android.ui.adapter.PhraseListAdapter; + +public class PhraseList extends BaseListEditor { + + public static Intent createIntent(Context context) { + return new Intent(context, PhraseList.class); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + } + + @Override + protected int getOptionsMenuId() { + return R.menu.add_phrase; + } + + @Override + protected int getAddActionId() { + return R.id.action_add_phrase; + } + + @Override + protected Intent getAddIntent() { + return PhraseAdder.createIntent(this); + } + + @Override + protected Intent getEditIntent(Integer actionWith) { + return PhraseEditor.createIntent(this, actionWith); + } + + @Override + protected int getRemoveTextResourceId() { + return R.string.phrase_delete; + } + + @Override + protected String getRemoveConfirmation(Integer actionWith) { + String text = PhraseManager.getInstance().getPhrase(actionWith) + .getText(); + if ("".equals(text)) + text = Application.getInstance().getString(R.string.phrase_empty); + return getString(R.string.phrase_delete_confirm, text); + } + + @Override + protected void removeItem(Integer actionWith) { + PhraseManager.getInstance().removePhrase(actionWith); + } + + @Override + protected BaseListEditorAdapter createListAdapter() { + return new PhraseListAdapter(this); + } + + @Override + protected Integer getSavedValue(Bundle bundle, String key) { + return bundle.getInt(key); + } + + @Override + protected void putSavedValue(Bundle bundle, String key, Integer actionWith) { + bundle.putInt(key, actionWith); + } + +} diff --git a/app/src/main/java/com/xabber/android/ui/preferences/PreferenceEditor.java b/app/src/main/java/com/xabber/android/ui/preferences/PreferenceEditor.java new file mode 100644 index 0000000000..ddf2238cfd --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/preferences/PreferenceEditor.java @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2013, Redsolution LTD. All rights reserved. + * + * This file is part of Xabber project; you can redistribute it and/or + * modify it under the terms of the GNU General Public License, Version 3. + * + * Xabber is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License, + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.xabber.android.ui.preferences; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.support.v7.widget.Toolbar; + +import com.xabber.android.R; +import com.xabber.android.data.SettingsManager; +import com.xabber.android.ui.helper.BarPainter; +import com.xabber.android.ui.helper.ManagedActivity; + +public class PreferenceEditor extends ManagedActivity + implements PreferencesFragment.OnPreferencesFragmentInteractionListener { + + private BarPainter barPainter; + + public static Intent createIntent(Context context) { + return new Intent(context, PreferenceEditor.class); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (isFinishing()) + return; + + setContentView(R.layout.activity_with_toolbar_and_container); + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_default); + setSupportActionBar(toolbar); + barPainter = new BarPainter(this, toolbar); + + + if (savedInstanceState == null) { + getFragmentManager().beginTransaction() + .add(R.id.fragment_container, new PreferencesFragment()).commit(); + } + + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + // Force request sound. This will set default value if not specified. + SettingsManager.eventsSound(); + SettingsManager.chatsAttentionSound(); + } + + @Override + protected void onResume() { + super.onResume(); + barPainter.setDefaultColor(); + } + + @Override + public String getVersionName() { + try { + return getPackageManager().getPackageInfo(getPackageName(), 0).versionName; + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + return ""; + } +} diff --git a/app/src/main/java/com/xabber/android/ui/preferences/PreferencesFragment.java b/app/src/main/java/com/xabber/android/ui/preferences/PreferencesFragment.java new file mode 100644 index 0000000000..7d4318ae31 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/preferences/PreferencesFragment.java @@ -0,0 +1,47 @@ +package com.xabber.android.ui.preferences; + + +import android.app.Activity; +import android.os.Bundle; +import android.preference.Preference; + +import com.xabber.android.R; +import com.xabber.android.ui.helper.PreferenceSummaryHelper; + +public class PreferencesFragment extends android.preference.PreferenceFragment { + + private OnPreferencesFragmentInteractionListener mListener; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + addPreferencesFromResource(R.xml.preference_editor); + + Preference about = getPreferenceScreen().findPreference(getString(R.string.preference_about_key)); + about.setSummary(getString(R.string.application_title_full) + "\n" + mListener.getVersionName()); + + PreferenceSummaryHelper.updateSummary(getPreferenceScreen()); + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + try { + mListener = (OnPreferencesFragmentInteractionListener) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity.toString() + + " must implement OnPreferencesFragmentInteractionListener"); + } + } + + @Override + public void onDetach() { + super.onDetach(); + mListener = null; + } + + public interface OnPreferencesFragmentInteractionListener { + String getVersionName(); + } +} diff --git a/app/src/main/java/com/xabber/android/ui/preferences/SecurityClearCertificateDialog.java b/app/src/main/java/com/xabber/android/ui/preferences/SecurityClearCertificateDialog.java new file mode 100644 index 0000000000..45101d08c4 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/preferences/SecurityClearCertificateDialog.java @@ -0,0 +1,25 @@ +package com.xabber.android.ui.preferences; + +import android.content.Context; +import android.preference.DialogPreference; +import android.util.AttributeSet; + +import com.xabber.android.data.connection.CertificateManager; +import com.xabber.android.data.connection.ConnectionManager; + + +public class SecurityClearCertificateDialog extends DialogPreference { + public SecurityClearCertificateDialog(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onDialogClosed(boolean positiveResult) { + super.onDialogClosed(positiveResult); + + if (positiveResult) { + CertificateManager.getInstance().removeCertificates(); + ConnectionManager.getInstance().updateConnections(true); + } + } +} diff --git a/app/src/main/java/com/xabber/android/ui/preferences/SecuritySettings.java b/app/src/main/java/com/xabber/android/ui/preferences/SecuritySettings.java new file mode 100644 index 0000000000..0eeee73777 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/preferences/SecuritySettings.java @@ -0,0 +1,37 @@ +package com.xabber.android.ui.preferences; + +import android.os.Bundle; +import android.support.v7.widget.Toolbar; + +import com.xabber.android.R; +import com.xabber.android.ui.helper.BarPainter; +import com.xabber.android.ui.helper.ManagedActivity; +import com.xabber.android.ui.helper.PreferenceSummaryHelper; + +public class SecuritySettings extends ManagedActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (isFinishing()) + return; + + setContentView(R.layout.activity_with_toolbar_and_container); + + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_default); + + setSupportActionBar(toolbar); + + BarPainter barPainter = new BarPainter(this, toolbar); + barPainter.setDefaultColor(); + + setTitle(PreferenceSummaryHelper.getPreferenceTitle(getString(R.string.preference_security))); + + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + if (savedInstanceState == null) { + getFragmentManager().beginTransaction() + .add(R.id.fragment_container, new SecuritySettingsFragment()).commit(); + } + } +} diff --git a/app/src/main/java/com/xabber/android/ui/preferences/SecuritySettingsFragment.java b/app/src/main/java/com/xabber/android/ui/preferences/SecuritySettingsFragment.java new file mode 100644 index 0000000000..a3e78f33d4 --- /dev/null +++ b/app/src/main/java/com/xabber/android/ui/preferences/SecuritySettingsFragment.java @@ -0,0 +1,17 @@ +package com.xabber.android.ui.preferences; + +import android.os.Bundle; + +import com.xabber.android.R; +import com.xabber.android.ui.helper.PreferenceSummaryHelper; + +public class SecuritySettingsFragment extends android.preference.PreferenceFragment { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + addPreferencesFromResource(R.xml.preference_security); + + PreferenceSummaryHelper.updateSummary(getPreferenceScreen()); + } +} diff --git a/app/src/main/java/com/xabber/android/ui/widget/NoDefaultSpinner.java b/app/src/main/java/com/xabber/android/ui/widget/NoDefaultSpinner.java index 1bc3971cb1..49af435a6b 100644 --- a/app/src/main/java/com/xabber/android/ui/widget/NoDefaultSpinner.java +++ b/app/src/main/java/com/xabber/android/ui/widget/NoDefaultSpinner.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -31,103 +31,103 @@ /** * A modified Spinner that doesn't automatically select the first entry in the * list. - * + *

* Shows the prompt if nothing is selected. - * + *

* Limitations: does not display prompt if the entry list is empty. - * + *

* http://stackoverflow.com/a/3427058 - * + * * @author emmby */ public class NoDefaultSpinner extends Spinner { - public NoDefaultSpinner(Context context) { - super(context); - } - - public NoDefaultSpinner(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public NoDefaultSpinner(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - public void setAdapter(SpinnerAdapter orig) { - final SpinnerAdapter adapter = newProxy(orig); - - super.setAdapter(adapter); - - try { - final Method m = AdapterView.class.getDeclaredMethod( - "setNextSelectedPositionInt", int.class); - m.setAccessible(true); - m.invoke(this, -1); - - final Method n = AdapterView.class.getDeclaredMethod( - "setSelectedPositionInt", int.class); - n.setAccessible(true); - n.invoke(this, -1); - - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - protected SpinnerAdapter newProxy(SpinnerAdapter obj) { - return (SpinnerAdapter) java.lang.reflect.Proxy.newProxyInstance(obj - .getClass().getClassLoader(), - new Class[] { SpinnerAdapter.class }, new SpinnerAdapterProxy( - obj)); - } - - /** - * Intercepts getView() to display the prompt if position < 0 - */ - protected class SpinnerAdapterProxy implements InvocationHandler { - - protected SpinnerAdapter obj; - protected Method getView; - - protected SpinnerAdapterProxy(SpinnerAdapter obj) { - this.obj = obj; - try { - this.getView = SpinnerAdapter.class.getMethod("getView", - int.class, View.class, ViewGroup.class); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - @Override - public Object invoke(Object proxy, Method m, Object[] args) - throws Throwable { - try { - return m.equals(getView) && (Integer) (args[0]) < 0 ? getView( - (Integer) args[0], (View) args[1], (ViewGroup) args[2]) - : m.invoke(obj, args); - } catch (InvocationTargetException e) { - throw e.getTargetException(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - protected View getView(int position, View convertView, ViewGroup parent) - throws IllegalAccessException { - if (position < 0) { - final TextView v = (TextView) ((LayoutInflater) getContext() - .getSystemService(Context.LAYOUT_INFLATER_SERVICE)) - .inflate(android.R.layout.simple_spinner_item, parent, - false); - v.setText(getPrompt()); - return v; - } - - return obj.getView(position, convertView, parent); - } - - } + public NoDefaultSpinner(Context context) { + super(context); + } + + public NoDefaultSpinner(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public NoDefaultSpinner(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + public void setAdapter(SpinnerAdapter orig) { + final SpinnerAdapter adapter = newProxy(orig); + + super.setAdapter(adapter); + + try { + final Method m = AdapterView.class.getDeclaredMethod( + "setNextSelectedPositionInt", int.class); + m.setAccessible(true); + m.invoke(this, -1); + + final Method n = AdapterView.class.getDeclaredMethod( + "setSelectedPositionInt", int.class); + n.setAccessible(true); + n.invoke(this, -1); + + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + protected SpinnerAdapter newProxy(SpinnerAdapter obj) { + return (SpinnerAdapter) java.lang.reflect.Proxy.newProxyInstance(obj + .getClass().getClassLoader(), + new Class[]{SpinnerAdapter.class}, new SpinnerAdapterProxy( + obj)); + } + + /** + * Intercepts getView() to display the prompt if position < 0 + */ + protected class SpinnerAdapterProxy implements InvocationHandler { + + protected SpinnerAdapter obj; + protected Method getView; + + protected SpinnerAdapterProxy(SpinnerAdapter obj) { + this.obj = obj; + try { + this.getView = SpinnerAdapter.class.getMethod("getView", + int.class, View.class, ViewGroup.class); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public Object invoke(Object proxy, Method m, Object[] args) + throws Throwable { + try { + return m.equals(getView) && (Integer) (args[0]) < 0 ? getView( + (Integer) args[0], (View) args[1], (ViewGroup) args[2]) + : m.invoke(obj, args); + } catch (InvocationTargetException e) { + throw e.getTargetException(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + protected View getView(int position, View convertView, ViewGroup parent) + throws IllegalAccessException { + if (position < 0) { + final TextView v = (TextView) ((LayoutInflater) getContext() + .getSystemService(Context.LAYOUT_INFLATER_SERVICE)) + .inflate(android.R.layout.simple_spinner_item, parent, + false); + v.setText(getPrompt()); + return v; + } + + return obj.getView(position, convertView, parent); + } + + } } diff --git a/app/src/main/java/com/xabber/android/ui/widget/PageSwitcher.java b/app/src/main/java/com/xabber/android/ui/widget/PageSwitcher.java deleted file mode 100644 index 6edf43b9a8..0000000000 --- a/app/src/main/java/com/xabber/android/ui/widget/PageSwitcher.java +++ /dev/null @@ -1,769 +0,0 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.xabber.android.ui.widget; - -import android.content.Context; -import android.database.DataSetObserver; -import android.os.Handler; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewConfiguration; -import android.view.ViewGroup; -import android.view.animation.Animation; -import android.view.animation.Animation.AnimationListener; -import android.view.animation.AnimationUtils; -import android.widget.Adapter; -import android.widget.ListView; -import android.widget.Scroller; - -import com.xabber.android.data.LogManager; -import com.xabber.android.ui.adapter.SaveStateAdapter; -import com.xabber.androiddev.R; - -/** - * Widget to switch between chat pages. - * - * Warning: This class is to be replaced. - * - * @author alexander.ivanov - * - */ -public class PageSwitcher extends ViewGroup implements AnimationListener { - - public static final boolean LOG = false; - - /** - * Delay before hide pages. - */ - private static final long PAGES_HIDDER_DELAY = 1000; - - /** - * Distance a touch can wander before we think the user is scrolling. - */ - private final int touchSlop; - - private final DataSetObserver dataSetObserver = new DataSetObserver() { - @Override - public void onChanged() { - dataChanged = true; - requestLayout(); - } - - @Override - public void onInvalidated() { - dataChanged = true; - requestLayout(); - } - }; - - private boolean dataChanged; - - private SaveStateAdapter adapter; - - private int widthMeasureSpec; - - private int heightMeasureSpec; - - /** - * User is currently dragging this view. - */ - private boolean isBeingDragged; - - /** - * Drag was interrupted. - */ - private boolean dragWasCanceled; - - /** - * Position of down touch. - */ - private float touchX; - - /** - * Initial scroll position. - */ - private int initialScrollX; - - /** - * {@link Scroller#isFinished()} is false when being flung. - */ - private final Scroller scroller; - - /** - * The listener that receives notifications when an item is selected, - * unselected or removed. - */ - private OnSelectListener onSelectListener; - - /** - * Selected position in adapter. Can be incorrect while {@link #dataChanged} - * is true . - */ - private int selectedPosition; - - private View selectedView; - - /** - * Previous selected object (before data changes). - */ - private Object previousSelectedObject; - - /** - * Visible but not selected position. - */ - private int visiblePosition; - - /** - * Visible but not selected view. - */ - private View visibleView; - - /** - * Previous visible object (before data changes). - */ - private Object previousVisibleObject; - - /** - * Animation used to hide pages. - */ - private final Animation pagesHideAnimation; - - /** - * Runnable called to hide pages. - */ - private final Runnable pagesHideRunnable = new Runnable() { - @Override - public void run() { - if (LOG) - LogManager.i(this, "hide pages"); - handler.removeCallbacks(this); - if (selectedView != null) - selectedView.findViewById(R.id.chat_page).startAnimation( - pagesHideAnimation); - if (visibleView != null) { - visibleView.findViewById(R.id.chat_page).setVisibility( - View.GONE); - visibleView.findViewById(R.id.chat_page).clearAnimation(); - } - } - }; - - /** - * Whether pages are shown. - */ - private boolean pagesShown; - - private final Handler handler; - - public PageSwitcher(Context context, AttributeSet attrs) { - super(context, attrs); - touchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); - dataChanged = true; - adapter = null; - widthMeasureSpec = 0; - heightMeasureSpec = 0; - isBeingDragged = false; - dragWasCanceled = false; - touchX = 0; - initialScrollX = 0; - scroller = new Scroller(getContext()); - onSelectListener = null; - - selectedPosition = 0; - selectedView = null; - previousSelectedObject = null; - visiblePosition = 0; - visibleView = null; - previousVisibleObject = null; - - handler = new Handler(); - pagesHideAnimation = AnimationUtils.loadAnimation(context, - R.anim.chat_page_out); - pagesHideAnimation.setAnimationListener(this); - pagesShown = false; - } - - /** - * Sets adapter. Request update and set initial selected position. - * - * @param adapter - */ - public void setAdapter(SaveStateAdapter adapter) { - if (adapter == null) - throw new IllegalStateException(); - if (this.adapter != null) - this.adapter.unregisterDataSetObserver(dataSetObserver); - this.adapter = adapter; - this.adapter.registerDataSetObserver(dataSetObserver); - // dataChanged will be set in setSelection(). - setSelection(0); - } - - /** - * @return Assigned adapter. - */ - public Adapter getAdapter() { - return adapter; - } - - /** - * Register a callback to be invoked when an item in this View has been - * selected, unselected or removed. - * - * @param listener - * The callback that will run - */ - public void setOnSelectListener(OnSelectListener listener) { - onSelectListener = listener; - } - - public OnSelectListener getOnSelectListener() { - return onSelectListener; - } - - /** - * @return Selected position in adapter. - */ - public int getSelectedItemPosition() { - if (adapter == null) - throw new IllegalStateException(); - return selectedPosition; - } - - /** - * @return Selected item in adapter or null if there is no - * elements. - */ - public Object getSelectedItem() { - if (adapter == null) - throw new IllegalStateException(); - if (selectedPosition < 0 || selectedPosition >= adapter.getCount()) - return null; - else - return adapter.getItem(selectedPosition); - } - - /** - * @return Visible item from adapter or null if there is no - * visible elements (selected element still can exists). - */ - public Object getVisibleItem() { - if (adapter == null) - throw new IllegalStateException(); - if (visiblePosition < 0 || visiblePosition >= adapter.getCount()) - return null; - else - return adapter.getItem(visiblePosition); - } - - /** - * @return Selected view. - */ - public View getSelectedView() { - if (adapter == null) - throw new IllegalStateException(); - return selectedView; - } - - /** - * @return Visible view. - */ - public View getVisibleView() { - if (adapter == null) - throw new IllegalStateException(); - return visibleView; - } - - /** - * @return number of items or zero if adapter is null . - */ - private int getCount() { - return adapter == null ? 0 : adapter.getCount(); - } - - /** - * Returns correct position. - * - * @param position - * @return value between 0 and count - 1. - */ - private int correntPosition(int position) { - final int count = getCount(); - if (position >= count) - return 0; - if (position < 0) - return count - 1; - return position; - } - - /** - * Gets view. - * - * @param position - * in adapter. - * @param x - * position in layout. - * @param convertView - * previous view. - * @param update - * whether we need force update underlying view. - * @param layout - * whether we need to update layout (remeasure). - * @return - */ - private View getView(int position, int x, View convertView, boolean update, - boolean layout) { - final View view; - if (convertView == null) { - if (LOG) - LogManager.i(this, "new view"); - view = adapter.getView(position, null, this); - } else if (update) { - if (LOG) - LogManager.i(this, "update view"); - view = adapter.getView(position, convertView, this); - } else { - view = convertView; - } - if (view != convertView) { - if (LOG) - LogManager.i(this, "init view"); - LayoutParams layoutParams = view.getLayoutParams(); - if (layoutParams == null) - layoutParams = new LayoutParams(LayoutParams.FILL_PARENT, - LayoutParams.FILL_PARENT); - addViewInLayout(view, 0, layoutParams, true); - } - if (update || layout || view.getLeft() != x) { - if (LOG) - LogManager.i(this, "layout view"); - // We must measure ListView after update to show items. - measureChild(view, widthMeasureSpec, heightMeasureSpec); - view.layout(x, 0, x + view.getMeasuredWidth(), - view.getMeasuredHeight()); - } - return view; - } - - /** - * Updates scrolling, creates views if necessary . - * - * @param layout - * whether we need to update layout (remeasure). - */ - private void update(boolean layout) { - // Process data change. - final int count = getCount(); - if (dataChanged) { - if (previousSelectedObject != null) - for (int position = 0; position < count; position++) - if (adapter.getItem(position) - .equals(previousSelectedObject)) { - selectedPosition = position; - if (LOG) - LogManager.i(this, "Found selected position: " - + selectedPosition); - break; - } - selectedPosition = correntPosition(selectedPosition); - } - - // Process scrolling. - final int width = getWidth(); - int scrollX = getScrollX(); - if (width != 0) { - while (scrollX >= width) { - scrollX -= width; - initialScrollX -= width; - selectedPosition = correntPosition(selectedPosition + 1); - if (LOG) - LogManager.i(this, "scrollX >= width: " + selectedPosition); - } - while (scrollX <= -width) { - scrollX += width; - initialScrollX += width; - selectedPosition = correntPosition(selectedPosition - 1); - if (LOG) - LogManager - .i(this, "scrollX <= -width: " + selectedPosition); - } - } - - // Process low count. - if (count < 2) { - if (LOG) - LogManager.i(this, "count < 2"); - dragWasCanceled = true; - isBeingDragged = false; - if (!scroller.isFinished()) - scroller.abortAnimation(); - if (scrollX != 0) - scrollX = 0; - } - - // Store focus. - final View focus; - if (selectedView != null) - focus = selectedView.findFocus(); - else - focus = null; - - // Process selected view. - if (count == 0) { - if (LOG) - LogManager.i(this, "count == 0"); - selectedPosition = -1; - if (selectedView != null) { - if (onSelectListener != null) - onSelectListener.onUnselect(); - adapter.saveState(selectedView); - removeViewInLayout(selectedView); - selectedView = null; - // We must invalidate to update view. - invalidate(); - } - } else { - if (LOG) - LogManager.i(this, "count > 0"); - - // Exchange visible and selected views and previous objects. - final Object selectedObject = adapter.getItem(selectedPosition); - final boolean exchange = previousSelectedObject != null - && previousVisibleObject != null - && !previousSelectedObject.equals(selectedObject) - && previousVisibleObject.equals(selectedObject); - if (exchange) { - Object tempObject = previousSelectedObject; - previousSelectedObject = previousVisibleObject; - previousVisibleObject = tempObject; - View view = selectedView; - selectedView = visibleView; - visibleView = view; - } - - // Update view. - final boolean update = dataChanged - || previousSelectedObject == null - || !previousSelectedObject.equals(selectedObject); - selectedView = getView(selectedPosition, 0, selectedView, update, - layout); - previousSelectedObject = selectedObject; - if (update || exchange) - if (onSelectListener != null) - onSelectListener.onSelect(); - - // Enable focusable. - if (selectedView instanceof ViewGroup) - ((ViewGroup) selectedView) - .setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); - else - selectedView.setFocusable(true); - } - - // Process visible (not selected) view. - if (count < 2) { - if (LOG) - LogManager.i(this, "count < 2 || scrollX == 0"); - visiblePosition = -1; - if (visibleView != null) { - adapter.saveState(visibleView); - removeViewInLayout(visibleView); - visibleView = null; - } - } else { - // Calculate position. - final int visibleX; - if (scrollX > 0) { - if (LOG) - LogManager.i(this, "scrollX > 0"); - visiblePosition = correntPosition(selectedPosition + 1); - visibleX = width; - } else { - if (LOG) - LogManager.i(this, "scrollX < 0"); - visiblePosition = correntPosition(selectedPosition - 1); - visibleX = -width; - } - - // Update view. - final Object visibleObject = adapter.getItem(visiblePosition); - final boolean update = dataChanged || previousVisibleObject == null - || !previousVisibleObject.equals(visibleObject); - visibleView = getView(visiblePosition, visibleX, visibleView, - update, layout); - previousVisibleObject = visibleObject; - - // Disable focusable. - if (visibleView instanceof ViewGroup) - ((ViewGroup) visibleView) - .setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); - else - visibleView.setFocusable(false); - } - - // Restore focus by ID. - if (selectedView != null) { - View target; - if (focus == null || focus.getId() == View.NO_ID) - target = null; - else - target = selectedView.findViewById(focus.getId()); - if (target == null) - target = selectedView.findViewById(R.id.chat_input); - target.requestFocus(); - } - - if (scrollX == 0) { - if (LOG) - LogManager.i(this, "Scroll X == 0"); - hidePages(); - } else { - if (LOG) - LogManager.i(this, "Scroll X != 0"); - showPages(); - } - - super.scrollTo(scrollX, 0); - - dataChanged = false; - } - - /** - * Show pages. - */ - private void showPages() { - if (pagesShown) - return; - pagesShown = true; - handler.removeCallbacks(pagesHideRunnable); - if (selectedView != null) { - selectedView.findViewById(R.id.chat_page).clearAnimation(); - selectedView.findViewById(R.id.chat_page).setVisibility( - View.VISIBLE); - } - if (visibleView != null) { - visibleView.findViewById(R.id.chat_page).clearAnimation(); - visibleView.findViewById(R.id.chat_page) - .setVisibility(View.VISIBLE); - } - } - - /** - * Requests pages to be hiden in future. - */ - private void hidePages() { - if (!pagesShown) - return; - pagesShown = false; - handler.postDelayed(pagesHideRunnable, PAGES_HIDDER_DELAY); - } - - /** - * Save state of views. Must be called on activity pause. - */ - public void saveState() { - if (visibleView != null) - adapter.saveState(visibleView); - if (selectedView != null) - adapter.saveState(selectedView); - } - - /** - * Selects an item. Immediately scroll to this position. - * - * @param position - */ - public void setSelection(int position) { - if (adapter == null) - throw new IllegalStateException(); - dataChanged = true; - isBeingDragged = false; - dragWasCanceled = true; - if (!scroller.isFinished()) - scroller.abortAnimation(); - if (getScrollX() != 0 || getScrollY() != 0) - super.scrollTo(0, 0); - previousSelectedObject = null; - selectedPosition = position; - if (LOG) - LogManager.i(this, "setSelection: " + selectedPosition); - update(false); - if (selectedView == null) - return; - ListView listView = (ListView) selectedView - .findViewById(android.R.id.list); - listView.setAdapter(listView.getAdapter()); - } - - /** - * Stop any movements. - */ - public void stopMovement() { - isBeingDragged = false; - dragWasCanceled = true; - if (!scroller.isFinished()) - scroller.abortAnimation(); - if (getScrollX() != 0 || getScrollY() != 0) - super.scrollTo(0, 0); - update(false); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - this.widthMeasureSpec = widthMeasureSpec; - this.heightMeasureSpec = heightMeasureSpec; - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, - int bottom) { - if (LOG) - LogManager.i(this, "onLayout"); - update(true); - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent event) { - if (getCount() <= 1) { - isBeingDragged = false; - dragWasCanceled = true; - return false; - } - - final int action = event.getAction(); - if (action == MotionEvent.ACTION_DOWN) - dragWasCanceled = false; - if (dragWasCanceled) - return false; - - final float x = event.getX(); - switch (action) { - case MotionEvent.ACTION_DOWN: - touchX = x; - initialScrollX = getScrollX(); - if (!scroller.isFinished()) { - scroller.abortAnimation(); - isBeingDragged = true; - } else { - isBeingDragged = false; - } - break; - case MotionEvent.ACTION_MOVE: - if (Math.abs(x - touchX) > touchSlop) - isBeingDragged = true; - // requestDisallowInterceptTouchEvent(true); - break; - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_UP: - break; - } - return isBeingDragged; - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - if (getCount() <= 1) - dragWasCanceled = true; - if (dragWasCanceled) - return false; - - final int action = event.getAction(); - final float x = event.getX(); - switch (action) { - case MotionEvent.ACTION_DOWN: - break; - case MotionEvent.ACTION_MOVE: - if (Math.abs(x - touchX) > touchSlop) - isBeingDragged = true; - if (isBeingDragged) { - if (LOG) - LogManager.i(this, "onTouchEvent - MOVE"); - scrollTo((int) (touchX - x) + initialScrollX, 0); - } - break; - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_UP: - if (isBeingDragged) { - final int scroll = getScrollX(); - final int width = getWidth(); - final int center = width / 2; - final int target; - if (scroll > center) { - target = width; - } else if (scroll < -center) { - target = -width; - } else { - target = 0; - } - scroller.startScroll(scroll, 0, target - scroll, 0); - invalidate(); - } - isBeingDragged = false; - break; - } - return true; - } - - @Override - public void computeScroll() { - if (scroller.computeScrollOffset()) { - if (scroller.getCurrX() == scroller.getFinalX() - && scroller.getCurrY() == scroller.getFinalY()) - scroller.abortAnimation(); - if (LOG) - LogManager.i(this, "computeScroll"); - scrollTo(scroller.getCurrX(), scroller.getCurrY()); - postInvalidate(); - } - } - - @Override - public void scrollTo(int x, int y) { - if (LOG) - LogManager.i(this, "scrollTo: " + x + "," + y); - super.scrollTo(x, y); - update(false); - } - - @Override - public void onAnimationStart(Animation animation) { - } - - @Override - public void onAnimationEnd(Animation animation) { - if (selectedView != null) - selectedView.findViewById(R.id.chat_page).setVisibility(View.GONE); - } - - @Override - public void onAnimationRepeat(Animation animation) { - } - - public interface OnSelectListener { - - /** - * Callback method to be invoked when an item has been selected. - */ - void onSelect(); - - /** - * Callback method to be invoked when an item has been unselected. - */ - void onUnselect(); - - } -} diff --git a/app/src/main/java/com/xabber/android/ui/widget/PriorityPreference.java b/app/src/main/java/com/xabber/android/ui/widget/PriorityPreference.java index dfca4fb5e4..6fa7b244a3 100644 --- a/app/src/main/java/com/xabber/android/ui/widget/PriorityPreference.java +++ b/app/src/main/java/com/xabber/android/ui/widget/PriorityPreference.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -19,59 +19,58 @@ import android.util.AttributeSet; import android.widget.Toast; -import com.xabber.androiddev.R; +import com.xabber.android.R; /** * Preference to validate xmpp priority input and to show related hint. - * + * * @author alexander.ivanov - * */ public class PriorityPreference extends EditTextPreference { - private final Context context; + private final Context context; - public PriorityPreference(Context context) { - super(context); - this.context = context; - } + public PriorityPreference(Context context) { + super(context); + this.context = context; + } - public PriorityPreference(Context context, AttributeSet attrs) { - super(context, attrs); - this.context = context; - } + public PriorityPreference(Context context, AttributeSet attrs) { + super(context, attrs); + this.context = context; + } - public PriorityPreference(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - this.context = context; - } + public PriorityPreference(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + this.context = context; + } - @Override - protected boolean callChangeListener(Object newValue) { - try { - int value = Integer.parseInt((String) newValue); - if (value < -128 || value > 128) - throw new NumberFormatException(); - } catch (NumberFormatException e) { - Toast.makeText(context, - context.getString(R.string.account_invalid_priority), - Toast.LENGTH_LONG).show(); - return false; - } - return super.callChangeListener(newValue); - } + @Override + protected boolean callChangeListener(Object newValue) { + try { + int value = Integer.parseInt((String) newValue); + if (value < -128 || value > 128) + throw new NumberFormatException(); + } catch (NumberFormatException e) { + Toast.makeText(context, + context.getString(R.string.account_invalid_priority), + Toast.LENGTH_LONG).show(); + return false; + } + return super.callChangeListener(newValue); + } - @Override - public void setText(String text) { - super.setText(text); - String summary = text; - try { - if (Integer.parseInt(text) < 0) - summary = context.getString(R.string.negative_priotiry_summary, - text); - } catch (NumberFormatException e) { - } - setSummary(summary); - } + @Override + public void setText(String text) { + super.setText(text); + String summary = text; + try { + if (Integer.parseInt(text) < 0) + summary = context.getString(R.string.negative_priotiry_summary, + text); + } catch (NumberFormatException e) { + } + setSummary(summary); + } } diff --git a/app/src/main/java/com/xabber/android/ui/widget/RingtonePreference.java b/app/src/main/java/com/xabber/android/ui/widget/RingtonePreference.java index dfbcc3cb93..63a1a15d38 100644 --- a/app/src/main/java/com/xabber/android/ui/widget/RingtonePreference.java +++ b/app/src/main/java/com/xabber/android/ui/widget/RingtonePreference.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,42 +20,41 @@ /** * Ringtone preference that store and retrieve its data from internal property. - * + * * @author alexander.ivanov - * */ public class RingtonePreference extends android.preference.RingtonePreference { - private Uri uri; + private Uri uri; - public RingtonePreference(Context context) { - super(context); - } + public RingtonePreference(Context context) { + super(context); + } - public RingtonePreference(Context context, AttributeSet attrs) { - super(context, attrs); - } + public RingtonePreference(Context context, AttributeSet attrs) { + super(context, attrs); + } - public RingtonePreference(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } + public RingtonePreference(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } - @Override - protected Uri onRestoreRingtone() { - return uri; - } + @Override + protected Uri onRestoreRingtone() { + return uri; + } - @Override - protected void onSaveRingtone(Uri ringtoneUri) { - uri = ringtoneUri; - } + @Override + protected void onSaveRingtone(Uri ringtoneUri) { + uri = ringtoneUri; + } - public Uri getUri() { - return uri; - } + public Uri getUri() { + return uri; + } - public void setUri(Uri uri) { - this.uri = uri; - } + public void setUri(Uri uri) { + this.uri = uri; + } } diff --git a/app/src/main/java/com/xabber/android/ui/widget/StatusPreference.java b/app/src/main/java/com/xabber/android/ui/widget/StatusPreference.java deleted file mode 100644 index 32c4d20b1a..0000000000 --- a/app/src/main/java/com/xabber/android/ui/widget/StatusPreference.java +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.xabber.android.ui.widget; - -import android.content.Context; -import android.preference.Preference; -import android.util.AttributeSet; -import android.view.View; -import android.widget.ImageView; - -import com.xabber.android.data.account.StatusMode; -import com.xabber.androiddev.R; - -/** - * Preference to show status mode icon. - * - * @author alexander.ivanov - * - */ -public class StatusPreference extends Preference { - - private StatusMode statusMode; - - public StatusPreference(Context context) { - super(context); - init(); - } - - public StatusPreference(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - public StatusPreference(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - init(); - } - - private void init() { - setWidgetLayoutResource(R.layout.preference_status_widget); - } - - public void setStatusMode(StatusMode statusMode) { - this.statusMode = statusMode; - } - - @Override - protected void onBindView(View view) { - super.onBindView(view); - ((ImageView) view.findViewById(R.id.status_mode)) - .setImageLevel(statusMode.getStatusLevel()); - } - -} diff --git a/app/src/main/java/com/xabber/android/utils/DummyCursor.java b/app/src/main/java/com/xabber/android/utils/DummyCursor.java index 380d02cf56..89df43661b 100644 --- a/app/src/main/java/com/xabber/android/utils/DummyCursor.java +++ b/app/src/main/java/com/xabber/android/utils/DummyCursor.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -24,189 +24,199 @@ public class DummyCursor implements Cursor { - @Override - public int getCount() { - return 0; - } - - @Override - public int getPosition() { - return 0; - } - - @Override - public boolean move(int paramInt) { - return false; - } - - @Override - public boolean moveToPosition(int paramInt) { - return false; - } - - @Override - public boolean moveToFirst() { - return false; - } - - @Override - public boolean moveToLast() { - return false; - } - - @Override - public boolean moveToNext() { - return false; - } - - @Override - public boolean moveToPrevious() { - return false; - } - - @Override - public boolean isFirst() { - return false; - } - - @Override - public boolean isLast() { - return false; - } - - @Override - public boolean isBeforeFirst() { - return false; - } - - @Override - public boolean isAfterLast() { - return false; - } - - @Override - public int getColumnIndex(String paramString) { - return 0; - } - - @Override - public int getColumnIndexOrThrow(String paramString) - throws IllegalArgumentException { - return 0; - } - - @Override - public String getColumnName(int paramInt) { - return null; - } - - @Override - public String[] getColumnNames() { - return null; - } - - @Override - public int getColumnCount() { - return 0; - } - - @Override - public byte[] getBlob(int paramInt) { - return null; - } - - @Override - public String getString(int paramInt) { - return null; - } - - @Override - public void copyStringToBuffer(int paramInt, - CharArrayBuffer paramCharArrayBuffer) { - } - - @Override - public short getShort(int paramInt) { - return 0; - } - - @Override - public int getInt(int paramInt) { - return 0; - } - - @Override - public long getLong(int paramInt) { - return 0; - } - - @Override - public float getFloat(int paramInt) { - return 0; - } - - @Override - public double getDouble(int paramInt) { - return 0; - } - - @Override - public boolean isNull(int paramInt) { - return false; - } - - @Override - public void deactivate() { - } - - @Override - public boolean requery() { - return false; - } - - @Override - public void close() { - } - - @Override - public boolean isClosed() { - return false; - } - - @Override - public void registerContentObserver(ContentObserver paramContentObserver) { - } - - @Override - public void unregisterContentObserver(ContentObserver paramContentObserver) { - } - - @Override - public void registerDataSetObserver(DataSetObserver paramDataSetObserver) { - } - - @Override - public void unregisterDataSetObserver(DataSetObserver paramDataSetObserver) { - } - - @Override - public void setNotificationUri(ContentResolver paramContentResolver, - Uri paramUri) { - } - - @Override - public boolean getWantsAllOnMoveCalls() { - return false; - } - - @Override - public Bundle getExtras() { - return null; - } - - @Override - public Bundle respond(Bundle paramBundle) { - return null; - } + @Override + public int getCount() { + return 0; + } + + @Override + public int getPosition() { + return 0; + } + + @Override + public boolean move(int paramInt) { + return false; + } + + @Override + public boolean moveToPosition(int paramInt) { + return false; + } + + @Override + public boolean moveToFirst() { + return false; + } + + @Override + public boolean moveToLast() { + return false; + } + + @Override + public boolean moveToNext() { + return false; + } + + @Override + public boolean moveToPrevious() { + return false; + } + + @Override + public boolean isFirst() { + return false; + } + + @Override + public boolean isLast() { + return false; + } + + @Override + public boolean isBeforeFirst() { + return false; + } + + @Override + public boolean isAfterLast() { + return false; + } + + @Override + public int getColumnIndex(String paramString) { + return 0; + } + + @Override + public int getColumnIndexOrThrow(String paramString) + throws IllegalArgumentException { + return 0; + } + + @Override + public String getColumnName(int paramInt) { + return null; + } + + @Override + public String[] getColumnNames() { + return null; + } + + @Override + public int getColumnCount() { + return 0; + } + + @Override + public byte[] getBlob(int paramInt) { + return null; + } + + @Override + public String getString(int paramInt) { + return null; + } + + @Override + public void copyStringToBuffer(int paramInt, + CharArrayBuffer paramCharArrayBuffer) { + } + + @Override + public short getShort(int paramInt) { + return 0; + } + + @Override + public int getInt(int paramInt) { + return 0; + } + + @Override + public long getLong(int paramInt) { + return 0; + } + + @Override + public float getFloat(int paramInt) { + return 0; + } + + @Override + public double getDouble(int paramInt) { + return 0; + } + + @Override + public boolean isNull(int paramInt) { + return false; + } + + @Override + public void deactivate() { + } + + @Override + public boolean requery() { + return false; + } + + @Override + public void close() { + } + + @Override + public boolean isClosed() { + return false; + } + + @Override + public void registerContentObserver(ContentObserver paramContentObserver) { + } + + @Override + public void unregisterContentObserver(ContentObserver paramContentObserver) { + } + + @Override + public void registerDataSetObserver(DataSetObserver paramDataSetObserver) { + } + + @Override + public void unregisterDataSetObserver(DataSetObserver paramDataSetObserver) { + } + + @Override + public void setNotificationUri(ContentResolver paramContentResolver, + Uri paramUri) { + } + + @Override + public Uri getNotificationUri() { + return null; + } + + @Override + public boolean getWantsAllOnMoveCalls() { + return false; + } + + @Override + public Bundle getExtras() { + return null; + } + + @Override + public Bundle respond(Bundle paramBundle) { + return null; + } + + @Override + public int getType(int arg0) { + return 0; + } } diff --git a/app/src/main/java/com/xabber/android/utils/Emoticons.java b/app/src/main/java/com/xabber/android/utils/Emoticons.java deleted file mode 100644 index 4e5f66523a..0000000000 --- a/app/src/main/java/com/xabber/android/utils/Emoticons.java +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * - * This file is part of Xabber project; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, Version 3. - * - * Xabber is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License, - * along with this program. If not, see http://www.gnu.org/licenses/. - */ -package com.xabber.android.utils; - -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import android.content.Context; -import android.text.Spannable; -import android.text.Spannable.Factory; -import android.text.style.ImageSpan; - -import com.xabber.android.data.SettingsManager; -import com.xabber.androiddev.R; - -/** - * Emoticons. - * - * @author alexander.ivanov - * - */ -public class Emoticons { - - public static final Map ANDROID_EMOTICONS = new HashMap(); - public static final Map NONE_EMOTICONS = new HashMap(); - - private static final Factory spannableFactory = Spannable.Factory - .getInstance(); - - static { - addPattern(ANDROID_EMOTICONS, ":)", R.drawable.emo_im_happy); - addPattern(ANDROID_EMOTICONS, ":-)", R.drawable.emo_im_happy); - addPattern(ANDROID_EMOTICONS, ":(", R.drawable.emo_im_sad); - addPattern(ANDROID_EMOTICONS, ":-(", R.drawable.emo_im_sad); - addPattern(ANDROID_EMOTICONS, ";)", R.drawable.emo_im_winking); - addPattern(ANDROID_EMOTICONS, ";-)", R.drawable.emo_im_winking); - addPattern(ANDROID_EMOTICONS, ":P", - R.drawable.emo_im_tongue_sticking_out); - addPattern(ANDROID_EMOTICONS, ":-P", - R.drawable.emo_im_tongue_sticking_out); - addPattern(ANDROID_EMOTICONS, "=-O", R.drawable.emo_im_surprised); - addPattern(ANDROID_EMOTICONS, ":*", R.drawable.emo_im_kissing); - addPattern(ANDROID_EMOTICONS, ":-*", R.drawable.emo_im_kissing); - addPattern(ANDROID_EMOTICONS, ":O", R.drawable.emo_im_wtf); - addPattern(ANDROID_EMOTICONS, ":-O", R.drawable.emo_im_wtf); - addPattern(ANDROID_EMOTICONS, "B)", R.drawable.emo_im_cool); - addPattern(ANDROID_EMOTICONS, "B-)", R.drawable.emo_im_cool); - addPattern(ANDROID_EMOTICONS, "8)", R.drawable.emo_im_cool); - addPattern(ANDROID_EMOTICONS, "8-)", R.drawable.emo_im_cool); - addPattern(ANDROID_EMOTICONS, ":$", R.drawable.emo_im_money_mouth); - addPattern(ANDROID_EMOTICONS, ":-$", R.drawable.emo_im_money_mouth); - addPattern(ANDROID_EMOTICONS, ":-!", R.drawable.emo_im_foot_in_mouth); - addPattern(ANDROID_EMOTICONS, ":-[", R.drawable.emo_im_embarrassed); - addPattern(ANDROID_EMOTICONS, "O:)", R.drawable.emo_im_angel); - addPattern(ANDROID_EMOTICONS, "O:-)", R.drawable.emo_im_angel); - addPattern(ANDROID_EMOTICONS, ":\\", R.drawable.emo_im_undecided); - addPattern(ANDROID_EMOTICONS, ":-\\", R.drawable.emo_im_undecided); - addPattern(ANDROID_EMOTICONS, ":'(", R.drawable.emo_im_crying); - addPattern(ANDROID_EMOTICONS, ":D", R.drawable.emo_im_laughing); - addPattern(ANDROID_EMOTICONS, ":-D", R.drawable.emo_im_laughing); - addPattern(ANDROID_EMOTICONS, "O_o", R.drawable.emo_im_wtf); - addPattern(ANDROID_EMOTICONS, "o_O", R.drawable.emo_im_wtf); - addPattern(ANDROID_EMOTICONS, ">:O", R.drawable.emo_im_yelling); - addPattern(ANDROID_EMOTICONS, ">:0", R.drawable.emo_im_yelling); - addPattern(ANDROID_EMOTICONS, ":S", R.drawable.emo_im_lips_are_sealed); - addPattern(ANDROID_EMOTICONS, ":-S", R.drawable.emo_im_lips_are_sealed); - } - - private static void addPattern(Map map, String smile, - int resource) { - map.put(Pattern.compile(Pattern.quote(smile)), resource); - } - - private Emoticons() { - } - - /** - * @param text - * @return new spannable. - */ - public static Spannable newSpannable(CharSequence text) { - return spannableFactory.newSpannable(text); - } - - /** - * @param context - * @param spannable - * @return Whether smiles have been added into spannable. - */ - public static boolean getSmiledText(Context context, Spannable spannable) { - boolean hasChanges = false; - Map emoticons = SettingsManager.interfaceSmiles(); - for (Entry entry : emoticons.entrySet()) { - Matcher matcher = entry.getKey().matcher(spannable); - while (matcher.find()) { - boolean set = true; - for (ImageSpan span : spannable.getSpans(matcher.start(), - matcher.end(), ImageSpan.class)) - if (spannable.getSpanStart(span) >= matcher.start() - && spannable.getSpanEnd(span) <= matcher.end()) - spannable.removeSpan(span); - else { - set = false; - break; - } - if (set) { - spannable.setSpan(new ImageSpan(context, entry.getValue()), - matcher.start(), matcher.end(), - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - hasChanges = true; - } - } - } - return hasChanges; - } - - /** - * @param context - * @param text - * @return New spannable with added smiles if needed. - */ - public static Spannable getSmiledText(Context context, CharSequence text) { - Spannable spannable = spannableFactory.newSpannable(text); - getSmiledText(context, spannable); - return spannable; - } - -} diff --git a/app/src/main/java/com/xabber/android/utils/StringUtils.java b/app/src/main/java/com/xabber/android/utils/StringUtils.java index 6e746c70c0..cbd4834020 100644 --- a/app/src/main/java/com/xabber/android/utils/StringUtils.java +++ b/app/src/main/java/com/xabber/android/utils/StringUtils.java @@ -1,137 +1,152 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.xabber.android.utils; +import android.content.Context; +import android.content.res.Resources; + import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Calendar; import java.util.Date; - -import android.content.res.Resources; +import java.util.GregorianCalendar; /** * Helper class to get plural forms. - * + * * @author alexander.ivanov - * */ public class StringUtils { - private static final DateFormat DATE_TIME; - private static final DateFormat TIME; + private static final DateFormat DATE_TIME; + private static final DateFormat TIME; + + static { + DATE_TIME = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, + DateFormat.SHORT); + TIME = new SimpleDateFormat("H:mm"); + } + + private StringUtils() { + } + + /** + * @param resources + * @param stringArrayResourceId + * @param quantity + * @return Plural string for the given quantity. + */ + public static String getQuantityString(Resources resources, + int stringArrayResourceId, long quantity) { + String[] strings = resources.getStringArray(stringArrayResourceId); + String lang = resources.getConfiguration().locale.getLanguage(); + if ("ru".equals(lang) && strings.length == 3) { + quantity = quantity % 100; + if (quantity >= 20) + quantity = quantity % 10; + if (quantity == 1) + return strings[0]; + if (quantity >= 2 && quantity < 5) + return strings[1]; + return strings[2]; + } else if (("cs".equals(lang) || "pl".equals(lang)) + && strings.length == 3) { + if (quantity == 1) { + return strings[0]; + } else if (quantity >= 2 && quantity <= 4) { + return strings[1]; + } else { + return strings[2]; + } + } else { + if (quantity == 1) { + return strings[0]; + } else { + return strings[1]; + } + } + } - static { - DATE_TIME = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, - DateFormat.SHORT); - TIME = DateFormat.getTimeInstance(DateFormat.MEDIUM); - } + /** + * Escape input chars to be shown in html. + * + * @param input + * @return + */ + public static String escapeHtml(String input) { + StringBuilder builder = new StringBuilder(); + int pos = 0; + int len = input.length(); + while (pos < len) { + int codePoint = Character.codePointAt(input, pos); + if (codePoint == '"') + builder.append("""); + else if (codePoint == '&') + builder.append("&"); + else if (codePoint == '<') + builder.append("<"); + else if (codePoint == '>') + builder.append(">"); + else if (codePoint == '\n') + builder.append("
"); + else if (codePoint >= 0 && codePoint < 160) + builder.append(Character.toChars(codePoint)); + else + builder.append("&#").append(codePoint).append(';'); + pos += Character.charCount(codePoint); + } + return builder.toString(); + } - private StringUtils() { - } + /** + * @param timeStamp + * @return String with date and time to be display. + */ + public static String getDateTimeText(Date timeStamp) { + synchronized (DATE_TIME) { + return DATE_TIME.format(timeStamp); + } + } - /** - * @param resources - * @param stringArrayResourceId - * @param quantity - * @return Plural string for the given quantity. - */ - public static String getQuantityString(Resources resources, - int stringArrayResourceId, long quantity) { - String[] strings = resources.getStringArray(stringArrayResourceId); - String lang = resources.getConfiguration().locale.getLanguage(); - if ("ru".equals(lang) && strings.length == 3) { - quantity = quantity % 100; - if (quantity >= 20) - quantity = quantity % 10; - if (quantity == 1) - return strings[0]; - if (quantity >= 2 && quantity < 5) - return strings[1]; - return strings[2]; - } else if (("cs".equals(lang) || "pl".equals(lang)) - && strings.length == 3) { - if (quantity == 1) { - return strings[0]; - } else if (quantity >= 2 && quantity <= 4) { - return strings[1]; - } else { - return strings[2]; - } - } else { - if (quantity == 1) { - return strings[0]; - } else { - return strings[1]; - } - } - } + /** + * @param timeStamp + * @return String with time or with date and time depend on current time. + */ + public static String getSmartTimeText(Context context, Date timeStamp) { + if (timeStamp == null) { + return ""; + } - /** - * Escape input chars to be shown in html. - * - * @param input - * @return - */ - public static String escapeHtml(String input) { - StringBuilder builder = new StringBuilder(); - int pos = 0; - int len = input.length(); - while (pos < len) { - int codePoint = Character.codePointAt(input, pos); - if (codePoint == '"') - builder.append("""); - else if (codePoint == '&') - builder.append("&"); - else if (codePoint == '<') - builder.append("<"); - else if (codePoint == '>') - builder.append(">"); - else if (codePoint == '\n') - builder.append("
"); - else if (codePoint >= 0 && codePoint < 160) - builder.append(Character.toChars(codePoint)); - else - builder.append("&#").append(codePoint).append(';'); - pos += Character.charCount(codePoint); - } - return builder.toString(); - } + // today + Calendar midnight = new GregorianCalendar(); + // reset hour, minutes, seconds and millis + midnight.set(Calendar.HOUR_OF_DAY, 0); + midnight.set(Calendar.MINUTE, 0); + midnight.set(Calendar.SECOND, 0); + midnight.set(Calendar.MILLISECOND, 0); - /** - * @param timeStamp - * @return String with date and time to be display. - */ - public static String getDateTimeText(Date timeStamp) { - synchronized (DATE_TIME) { - return DATE_TIME.format(timeStamp); - } - } + DateFormat timeFormat = android.text.format.DateFormat.getTimeFormat(context); - /** - * @param timeStamp - * @return String with time or with date and time depend on current time. - */ - public static String getSmartTimeText(Date timeStamp) { - if (timeStamp == null) - return ""; - Date date = new Date(); - long delta = date.getTime() - timeStamp.getTime(); - if (delta < 20 * 60 * 60 * 1000) - synchronized (TIME) { - return TIME.format(timeStamp); - } - else - return getDateTimeText(timeStamp); - } + if (timeStamp.getTime() > midnight.getTimeInMillis()) { + synchronized (TIME) { + return timeFormat.format(timeStamp); + } + } else { + DateFormat dateFormat = android.text.format.DateFormat.getDateFormat(context); + return dateFormat.format(timeStamp) + " " + timeFormat.format(timeStamp); + } + } } diff --git a/app/src/main/java/com/xabber/xmpp/AbstractExtensionProvider.java b/app/src/main/java/com/xabber/xmpp/AbstractExtensionProvider.java index 66db7ac983..293bf2f450 100644 --- a/app/src/main/java/com/xabber/xmpp/AbstractExtensionProvider.java +++ b/app/src/main/java/com/xabber/xmpp/AbstractExtensionProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -19,17 +19,16 @@ /** * Provides common interface to parse extensions. - * - * @author alexander.ivanov - * + * * @param + * @author alexander.ivanov */ public abstract class AbstractExtensionProvider - extends AbstractProvider implements PacketExtensionProvider { + extends AbstractProvider implements PacketExtensionProvider { - @Override - public Extension parseExtension(XmlPullParser parser) throws Exception { - return provideInstance(parser); - } + @Override + public Extension parseExtension(XmlPullParser parser) throws Exception { + return provideInstance(parser); + } } diff --git a/app/src/main/java/com/xabber/xmpp/AbstractIQProvider.java b/app/src/main/java/com/xabber/xmpp/AbstractIQProvider.java index 9d1369064e..97a9e2d0e1 100644 --- a/app/src/main/java/com/xabber/xmpp/AbstractIQProvider.java +++ b/app/src/main/java/com/xabber/xmpp/AbstractIQProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -19,17 +19,16 @@ /** * Provides common interface to parse iq packets. - * - * @author alexander.ivanov - * + * * @param + * @author alexander.ivanov */ public abstract class AbstractIQProvider extends - AbstractProvider implements IQProvider { + AbstractProvider implements IQProvider { - @Override - public IQ parseIQ(XmlPullParser parser) throws Exception { - return provideInstance(parser); - } + @Override + public IQ parseIQ(XmlPullParser parser) throws Exception { + return provideInstance(parser); + } } diff --git a/app/src/main/java/com/xabber/xmpp/AbstractInflater.java b/app/src/main/java/com/xabber/xmpp/AbstractInflater.java index d3f77187dc..3c5807de12 100644 --- a/app/src/main/java/com/xabber/xmpp/AbstractInflater.java +++ b/app/src/main/java/com/xabber/xmpp/AbstractInflater.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,85 +18,81 @@ /** * Provide common interface to populate java object from received xml. - * - * @author alexander.ivanov - * + * * @param + * @author alexander.ivanov */ public abstract class AbstractInflater { - /** - * Updates instance from tag name or attributes. - * - * Parser position mustn't be changed. - * - * @param instance - * @param parser - * @return modified instance. - */ - protected T preProcess(XmlPullParser parser, T instance) { - return instance; - } + /** + * Updates instance from tag name or attributes. + *

+ * Parser position mustn't be changed. + * + * @param instance + * @param parser + * @return modified instance. + */ + protected T preProcess(XmlPullParser parser, T instance) { + return instance; + } - /** - * Called when packet have been fully parsed. - * - * Parser position mustn't be changed. - * - * @param instance - * @return modified instance. - */ - protected T postProcess(T instance) throws Exception { - return instance; - } + /** + * Called when packet have been fully parsed. + *

+ * Parser position mustn't be changed. + * + * @param instance + * @return modified instance. + */ + protected T postProcess(T instance) throws Exception { + return instance; + } - /** - * Parses inner tag. - * - * Parser position either must be move to the end of processed tag, - * either mustn't be changed at all. - * - * @param parser - * @param instance - * @return Whether parser position have been changed. - * @throws Exception - */ - protected boolean parseInner(XmlPullParser parser, T instance) - throws Exception { - return false; - } + /** + * Parses inner tag. + *

+ * Parser position either must be move to the end of processed tag, + * either mustn't be changed at all. + * + * @param parser + * @param instance + * @return Whether parser position have been changed. + * @throws Exception + */ + protected boolean parseInner(XmlPullParser parser, T instance) + throws Exception { + return false; + } - /** - * Parse XML tag and populate element instance. At the beginning of the - * method call, the XML parser will be positioned on the opening tag. At the - * end of the method call, the parser must be positioned on the end - * of processed tag. - * - * @param parser - * an XML parser. - * @param instance - * instance to be updated. - * @return updated or replaced instance. - * @throws Exception - * if an error occurs parsing the XML. - */ - public T parseTag(XmlPullParser parser, T instance) throws Exception { - String name = parser.getName(); - instance = preProcess(parser, instance); - while (true) { - int eventType = parser.next(); - if (eventType == XmlPullParser.START_TAG) { - if (!parseInner(parser, instance)) - ProviderUtils.skipTag(parser); - } else if (eventType == XmlPullParser.END_TAG) { - if (name.equals(parser.getName())) - break; - else - throw new IllegalStateException(); - } else if (eventType == XmlPullParser.END_DOCUMENT) - break; - } - return postProcess(instance); - } + /** + * Parse XML tag and populate element instance. At the beginning of the + * method call, the XML parser will be positioned on the opening tag. At the + * end of the method call, the parser must be positioned on the end + * of processed tag. + * + * @param parser an XML parser. + * @param instance instance to be updated. + * @return updated or replaced instance. + * @throws Exception if an error occurs parsing the XML. + */ + public T parseTag(XmlPullParser parser, T instance) throws Exception { + String name = parser.getName(); + instance = preProcess(parser, instance); + while (true) { + int eventType = parser.next(); + if (eventType == XmlPullParser.START_TAG) { + if (!parseInner(parser, instance)) + ProviderUtils.skipTag(parser); + } else if (eventType == XmlPullParser.END_TAG) { + if (name.equals(parser.getName())) + break; + else + throw new IllegalStateException(); + } else if (eventType == XmlPullParser.END_DOCUMENT) + break; + } + return postProcess(instance); + } } diff --git a/app/src/main/java/com/xabber/xmpp/AbstractProvider.java b/app/src/main/java/com/xabber/xmpp/AbstractProvider.java index e2a6be30ab..636e93ab33 100644 --- a/app/src/main/java/com/xabber/xmpp/AbstractProvider.java +++ b/app/src/main/java/com/xabber/xmpp/AbstractProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,36 +18,33 @@ /** * Provide common interface to create new object from received XML. - * - * @author alexander.ivanov - * + * * @param + * @author alexander.ivanov */ public abstract class AbstractProvider extends - AbstractInflater { + AbstractInflater { - /** - * Creates an instance. - * - * Parser position mustn't be changed. - * - * @param parser - * @return - */ - abstract protected T createInstance(XmlPullParser parser); + /** + * Creates an instance. + *

+ * Parser position mustn't be changed. + * + * @param parser + * @return + */ + abstract protected T createInstance(XmlPullParser parser); - /** - * Parse XML tag and create instance. - * - * @param parser - * an XML parser. - * @return new instance. - * @throws Exception - * if an error occurs while parsing. - */ - public T provideInstance(XmlPullParser parser) throws Exception { - T instance = createInstance(parser); - return parseTag(parser, instance); - } + /** + * Parse XML tag and create instance. + * + * @param parser an XML parser. + * @return new instance. + * @throws Exception if an error occurs while parsing. + */ + public T provideInstance(XmlPullParser parser) throws Exception { + T instance = createInstance(parser); + return parseTag(parser, instance); + } } diff --git a/app/src/main/java/com/xabber/xmpp/Container.java b/app/src/main/java/com/xabber/xmpp/Container.java index 6771a702c6..526cbabca5 100644 --- a/app/src/main/java/com/xabber/xmpp/Container.java +++ b/app/src/main/java/com/xabber/xmpp/Container.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,29 +20,28 @@ /** * Representation of XML tag. - * + * * @author alexander.ivanov - * */ public interface Container extends Instance { - /** - * @return XML element name. - */ - public String getElementName(); + /** + * @return XML element name. + */ + String getElementName(); - /** - * @return XML namespace. - */ - public String getNamespace(); + /** + * @return XML namespace. + */ + String getNamespace(); - /** - * Serializes an inner content of XML tag. - * - * @param serializer - * @throws IOException - */ - public abstract void serializeContent(XmlSerializer serializer) - throws IOException; + /** + * Serializes an inner content of XML tag. + * + * @param serializer + * @throws IOException + */ + void serializeContent(XmlSerializer serializer) + throws IOException; } diff --git a/app/src/main/java/com/xabber/xmpp/IQ.java b/app/src/main/java/com/xabber/xmpp/IQ.java index c1929e785d..dd428a15b6 100644 --- a/app/src/main/java/com/xabber/xmpp/IQ.java +++ b/app/src/main/java/com/xabber/xmpp/IQ.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,23 +20,22 @@ /** * IQ packet. - * + *

* Note: we are going to remove underlying smack package. - * + * * @author alexander.ivanov - * */ public abstract class IQ extends org.jivesoftware.smack.packet.IQ implements - Container { + Container { - @Override - public void serialize(XmlSerializer serializer) throws IOException { - SerializerUtils.serialize(serializer, this); - } + @Override + public void serialize(XmlSerializer serializer) throws IOException { + SerializerUtils.serialize(serializer, this); + } - @Override - public String getChildElementXML() { - return SerializerUtils.toXml(this); - } + @Override + public String getChildElementXML() { + return SerializerUtils.toXml(this); + } } diff --git a/app/src/main/java/com/xabber/xmpp/Instance.java b/app/src/main/java/com/xabber/xmpp/Instance.java index 89687a5c74..30f9160c4e 100644 --- a/app/src/main/java/com/xabber/xmpp/Instance.java +++ b/app/src/main/java/com/xabber/xmpp/Instance.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,27 +20,26 @@ /** * Representation of some XML instance. - * + * * @author alexander.ivanov - * */ public interface Instance { - // TODO: Invalidate iq packets and packet extensions shouldn't be delivered - // to the ConnectionManager. + // TODO: Invalidate iq packets and packet extensions shouldn't be delivered + // to the ConnectionManager. - /** - * @return Whether parsed instance has valid values (e.g. required field are - * not null). - */ - public boolean isValid(); + /** + * @return Whether parsed instance has valid values (e.g. required field are + * not null). + */ + boolean isValid(); - /** - * Serializes an instance into XML using serializer. - * - * @param serializer - * @throws IOException - */ - public void serialize(XmlSerializer serializer) throws IOException; + /** + * Serializes an instance into XML using serializer. + * + * @param serializer + * @throws IOException + */ + void serialize(XmlSerializer serializer) throws IOException; } diff --git a/app/src/main/java/com/xabber/xmpp/OverflowReceiverBufferException.java b/app/src/main/java/com/xabber/xmpp/OverflowReceiverBufferException.java index 2ba7a960a6..58eed7dfea 100644 --- a/app/src/main/java/com/xabber/xmpp/OverflowReceiverBufferException.java +++ b/app/src/main/java/com/xabber/xmpp/OverflowReceiverBufferException.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,12 +16,11 @@ /** * Exception thrown when too many bytes received. - * + * * @author alexander.ivanov - * */ public class OverflowReceiverBufferException extends Exception { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; } diff --git a/app/src/main/java/com/xabber/xmpp/PacketExtension.java b/app/src/main/java/com/xabber/xmpp/PacketExtension.java index 6f4f0cee14..fa75c980b8 100644 --- a/app/src/main/java/com/xabber/xmpp/PacketExtension.java +++ b/app/src/main/java/com/xabber/xmpp/PacketExtension.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,23 +20,22 @@ /** * Packet extension. - * + *

* Note: we are going to remove underlying smack package. - * + * * @author alexander.ivanov - * */ public abstract class PacketExtension implements Container, - org.jivesoftware.smack.packet.PacketExtension { + org.jivesoftware.smack.packet.PacketExtension { - @Override - public void serialize(XmlSerializer serializer) throws IOException { - SerializerUtils.serialize(serializer, this); - } + @Override + public void serialize(XmlSerializer serializer) throws IOException { + SerializerUtils.serialize(serializer, this); + } - @Override - public String toXML() { - return SerializerUtils.toXml(this); - } + @Override + public String toXML() { + return SerializerUtils.toXml(this); + } } diff --git a/app/src/main/java/com/xabber/xmpp/ProviderUtils.java b/app/src/main/java/com/xabber/xmpp/ProviderUtils.java index fd0fcf578d..78367c4700 100644 --- a/app/src/main/java/com/xabber/xmpp/ProviderUtils.java +++ b/app/src/main/java/com/xabber/xmpp/ProviderUtils.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -31,224 +31,220 @@ /** * Set of functions commonly used by packet providers. - * + * * @author alexander.ivanov - * */ public class ProviderUtils { - private ProviderUtils() { - } - - /** - * Pattern to remove microseconds. - */ - private static final Pattern pattern = Pattern - .compile("^(\\d+-\\d+-\\d+T\\d+:\\d+:\\d+\\.\\d{1,3})\\d+(Z)$"); - - /** - * Date format without milliseconds. - */ - private static final DateFormat XEP_0082_UTC_FORMAT_WITHOUT_MILLIS = new SimpleDateFormat( - "yyyy-MM-dd'T'HH:mm:ss'Z'"); - - static { - XEP_0082_UTC_FORMAT_WITHOUT_MILLIS.setTimeZone(TimeZone - .getTimeZone("UTC")); - } - - /** - * Parse date time from string. - * - * @param dateString - * @return null if dateString is null or contains invalid data. - */ - public static Date parseDateTime(String dateString) { - if (dateString == null) - return null; - Matcher matcher = pattern.matcher(dateString); - if (matcher.matches()) - dateString = matcher.group(1) + matcher.group(2); - try { - return StringUtils.parseXEP0082Date(dateString); - } catch (ParseException e) { - synchronized (XEP_0082_UTC_FORMAT_WITHOUT_MILLIS) { - try { - return XEP_0082_UTC_FORMAT_WITHOUT_MILLIS.parse(dateString); - } catch (ParseException e2) { - return null; - } - } - } - } - - /** - * Parse integer. - * - * @param value - * @return null if inner value is null or invalid. - */ - public static Integer parseInteger(String value) { - try { - return Integer.valueOf(value); - } catch (NumberFormatException e) { - return null; - } - } - - /** - * Parse boolean from string. - * - * @param value - * @return null if value is null or invalid. - */ - public static Boolean parseBoolean(String value) { - if ("1".equals(value) || "true".equalsIgnoreCase(value)) - return true; - if ("0".equals(value) || "false".equalsIgnoreCase(value)) - return false; - return null; - } - - /** - * Returns string with text from all inner elements. - * - * @param parser - * @return Empty string if there is no inner text elements. - * @throws Exception - */ - public static String parseText(XmlPullParser parser) throws Exception { - return parseText(parser, -1); - } - - /** - * Returns string with text from all inner elements. - * - * @param parser - * @param maximum - * maximum length of returned value. Use -1 to - * disable limits. - * @return Empty string if there is no inner text elements. - * @throws OverflowReceiverBufferException - * If more than maximum chars have been read. Though parser - * position will be at the and of the tag. - * @throws Exception - */ - public static String parseText(XmlPullParser parser, int maximum) - throws OverflowReceiverBufferException, Exception { - final StringBuilder text = new StringBuilder(); - int inner = 1; - boolean overflow = false; - while (inner > 0) { - int eventType; - try { - eventType = parser.next(); - } catch (OutOfMemoryError e) { - LogManager.exception(parser, new RuntimeException(e)); - overflow = true; - continue; - } - if (eventType == XmlPullParser.TEXT) { - if (overflow) - continue; - String next = parser.getText(); - if (maximum != -1 && (text.length() + next.length()) > maximum) { - overflow = true; - continue; - } - try { - text.append(next); - } catch (OutOfMemoryError e) { - LogManager.exception(parser, new RuntimeException(e)); - overflow = true; - } - } else if (eventType == XmlPullParser.START_TAG) { - inner += 1; - } else if (eventType == XmlPullParser.END_TAG) { - inner -= 1; - } else if (eventType == XmlPullParser.END_DOCUMENT) - break; - } - if (overflow) - throw new OverflowReceiverBufferException(); - return text.toString(); - } - - /** - * Skip tag and its descendants. - * - * @param parser - * @throws IllegalStateException - * If closed tags are incompatible with opened one. - * @throws Exception - */ - public static void skipTag(XmlPullParser parser) - throws IllegalStateException, Exception { - LinkedList tags = new LinkedList(); - tags.add(parser.getName()); - while (!tags.isEmpty()) { - int eventType = parser.next(); - if (eventType == XmlPullParser.START_TAG) { - tags.add(parser.getName()); - } else if (eventType == XmlPullParser.END_TAG) { - if (!tags.removeLast().equals(parser.getName())) - throw new IllegalStateException(); - } else if (eventType == XmlPullParser.END_DOCUMENT) - break; - } - } - - /** - * Parse big decimal. - * - * @param parser - * @return null if inner text elements contains no or invalid - * data. - * @throws Exception - */ - public static BigDecimal parseBigDecimal(XmlPullParser parser) - throws Exception { - try { - return new BigDecimal(parseText(parser, -1)); - } catch (NumberFormatException e) { - return null; - } - } - - /** - * Parse integer. - * - * @param parser - * @return null if inner text elements contains no or invalid - * data. - * @throws Exception - */ - public static Integer parseInteger(XmlPullParser parser) throws Exception { - return parseInteger(parseText(parser, -1)); - } - - /** - * Parse boolean. - * - * @param parser - * @return null if inner text elements contains no or invalid - * data. - * @throws Exception - */ - public static Integer parseBoolean(XmlPullParser parser) throws Exception { - return parseInteger(parseText(parser, -1)); - } - - /** - * Parse date time. - * - * @param parser - * @return null if inner text elements contains no or invalid - * data. - * @throws Exception - */ - public static Date parseDateTime(XmlPullParser parser) throws Exception { - return parseDateTime(parseText(parser, -1)); - } + private ProviderUtils() { + } + + /** + * Pattern to remove microseconds. + */ + private static final Pattern pattern = Pattern + .compile("^(\\d+-\\d+-\\d+T\\d+:\\d+:\\d+\\.\\d{1,3})\\d+(Z)$"); + + /** + * Date format without milliseconds. + */ + private static final DateFormat XEP_0082_UTC_FORMAT_WITHOUT_MILLIS = new SimpleDateFormat( + "yyyy-MM-dd'T'HH:mm:ss'Z'"); + + static { + XEP_0082_UTC_FORMAT_WITHOUT_MILLIS.setTimeZone(TimeZone + .getTimeZone("UTC")); + } + + /** + * Parse date time from string. + * + * @param dateString + * @return null if dateString is null or contains invalid data. + */ + public static Date parseDateTime(String dateString) { + if (dateString == null) + return null; + Matcher matcher = pattern.matcher(dateString); + if (matcher.matches()) + dateString = matcher.group(1) + matcher.group(2); + try { + return StringUtils.parseXEP0082Date(dateString); + } catch (ParseException e) { + synchronized (XEP_0082_UTC_FORMAT_WITHOUT_MILLIS) { + try { + return XEP_0082_UTC_FORMAT_WITHOUT_MILLIS.parse(dateString); + } catch (ParseException e2) { + return null; + } + } + } + } + + /** + * Parse integer. + * + * @param value + * @return null if inner value is null or invalid. + */ + public static Integer parseInteger(String value) { + try { + return Integer.valueOf(value); + } catch (NumberFormatException e) { + return null; + } + } + + /** + * Parse boolean from string. + * + * @param value + * @return null if value is null or invalid. + */ + public static Boolean parseBoolean(String value) { + if ("1".equals(value) || "true".equalsIgnoreCase(value)) + return true; + if ("0".equals(value) || "false".equalsIgnoreCase(value)) + return false; + return null; + } + + /** + * Returns string with text from all inner elements. + * + * @param parser + * @return Empty string if there is no inner text elements. + * @throws Exception + */ + public static String parseText(XmlPullParser parser) throws Exception { + return parseText(parser, -1); + } + + /** + * Returns string with text from all inner elements. + * + * @param parser + * @param maximum maximum length of returned value. Use -1 to + * disable limits. + * @return Empty string if there is no inner text elements. + * @throws OverflowReceiverBufferException If more than maximum chars have been read. Though parser + * position will be at the and of the tag. + * @throws Exception + */ + public static String parseText(XmlPullParser parser, int maximum) + throws Exception { + final StringBuilder text = new StringBuilder(); + int inner = 1; + boolean overflow = false; + while (inner > 0) { + int eventType; + try { + eventType = parser.next(); + } catch (OutOfMemoryError e) { + LogManager.exception(parser, new RuntimeException(e)); + overflow = true; + continue; + } + if (eventType == XmlPullParser.TEXT) { + if (overflow) + continue; + String next = parser.getText(); + if (maximum != -1 && (text.length() + next.length()) > maximum) { + overflow = true; + continue; + } + try { + text.append(next); + } catch (OutOfMemoryError e) { + LogManager.exception(parser, new RuntimeException(e)); + overflow = true; + } + } else if (eventType == XmlPullParser.START_TAG) { + inner += 1; + } else if (eventType == XmlPullParser.END_TAG) { + inner -= 1; + } else if (eventType == XmlPullParser.END_DOCUMENT) + break; + } + if (overflow) + throw new OverflowReceiverBufferException(); + return text.toString(); + } + + /** + * Skip tag and its descendants. + * + * @param parser + * @throws IllegalStateException If closed tags are incompatible with opened one. + * @throws Exception + */ + public static void skipTag(XmlPullParser parser) + throws Exception { + LinkedList tags = new LinkedList(); + tags.add(parser.getName()); + while (!tags.isEmpty()) { + int eventType = parser.next(); + if (eventType == XmlPullParser.START_TAG) { + tags.add(parser.getName()); + } else if (eventType == XmlPullParser.END_TAG) { + if (!tags.removeLast().equals(parser.getName())) + throw new IllegalStateException(); + } else if (eventType == XmlPullParser.END_DOCUMENT) + break; + } + } + + /** + * Parse big decimal. + * + * @param parser + * @return null if inner text elements contains no or invalid + * data. + * @throws Exception + */ + public static BigDecimal parseBigDecimal(XmlPullParser parser) + throws Exception { + try { + return new BigDecimal(parseText(parser, -1)); + } catch (NumberFormatException e) { + return null; + } + } + + /** + * Parse integer. + * + * @param parser + * @return null if inner text elements contains no or invalid + * data. + * @throws Exception + */ + public static Integer parseInteger(XmlPullParser parser) throws Exception { + return parseInteger(parseText(parser, -1)); + } + + /** + * Parse boolean. + * + * @param parser + * @return null if inner text elements contains no or invalid + * data. + * @throws Exception + */ + public static Integer parseBoolean(XmlPullParser parser) throws Exception { + return parseInteger(parseText(parser, -1)); + } + + /** + * Parse date time. + * + * @param parser + * @return null if inner text elements contains no or invalid + * data. + * @throws Exception + */ + public static Date parseDateTime(XmlPullParser parser) throws Exception { + return parseDateTime(parseText(parser, -1)); + } } diff --git a/app/src/main/java/com/xabber/xmpp/SerializerUtils.java b/app/src/main/java/com/xabber/xmpp/SerializerUtils.java index f92b81a7d0..8e251aa74e 100644 --- a/app/src/main/java/com/xabber/xmpp/SerializerUtils.java +++ b/app/src/main/java/com/xabber/xmpp/SerializerUtils.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -26,153 +26,152 @@ /** * Set of functions commonly used by packet writers. - * + * * @author alexander.ivanov - * */ public final class SerializerUtils { - private SerializerUtils() { - } - - /** - * Returned packet as string with xml. String is ready to be written to the - * stream. - * - * @param instance - * @return - */ - public static String toXml(Instance instance) { - Writer writer = new StringWriter(); - XmlSerializer serializer = Xml.newSerializer(); - try { - serializer.setOutput(writer); - instance.serialize(serializer); - serializer.flush(); - } catch (IOException e) { - return ""; - } - return writer.toString(); - } - - /** - * Serialize container using its element name, namespace and content. - * - * @param serializer - * @param container - * @throws IOException - */ - public static void serialize(XmlSerializer serializer, Container container) - throws IOException { - serializer.setPrefix("", container.getNamespace()); - serializer.startTag(container.getNamespace(), - container.getElementName()); - container.serializeContent(serializer); - serializer.endTag(container.getNamespace(), container.getElementName()); - } - - /** - * Adds inner tag. - * - * @param serializer - * @param elementName - * @throws IOException - */ - public static void addEmtpyTag(XmlSerializer serializer, String elementName) - throws IOException { - serializer.startTag(null, elementName); - serializer.endTag(null, elementName); - } - - /** - * Adds inner tag with text payload. - * - * @param serializer - * @param elementName - * @param innerValue - * @throws IOException - */ - public static void addTextTag(XmlSerializer serializer, String elementName, - String innerValue) throws IOException { - serializer.startTag(null, elementName); - serializer.text(innerValue); - serializer.endTag(null, elementName); - } - - public static void addDateTimeTag(XmlSerializer serializer, - String elementName, Date innerValue) throws IOException { - addTextTag(serializer, elementName, serializeDateTime(innerValue)); - } - - public static void addIntegerTag(XmlSerializer serializer, - String elementName, Integer innerValue) throws IOException { - addTextTag(serializer, elementName, serializeInteger(innerValue)); - } - - public static void addBooleanTag(XmlSerializer serializer, - String elementName, Boolean innerValue) throws IOException { - addTextTag(serializer, elementName, serializeBoolean(innerValue)); - } - - /** - * Sets attribute. - * - * @param serializer - * @param attributeName - * @param value - * @throws IOException - */ - public static void setTextAttribute(XmlSerializer serializer, - String attributeName, String value) throws IOException { - serializer.attribute(null, attributeName, value); - } - - public static void setDateTimeAttribute(XmlSerializer serializer, - String attributeName, Date value) throws IOException { - setTextAttribute(serializer, attributeName, serializeDateTime(value)); - } - - public static void setIntegerAttribute(XmlSerializer serializer, - String attributeName, Integer value) throws IOException { - setTextAttribute(serializer, attributeName, serializeInteger(value)); - } - - public static void setBooleanAttribute(XmlSerializer serializer, - String attributeName, Boolean value) throws IOException { - setTextAttribute(serializer, attributeName, serializeBoolean(value)); - } - - /** - * Creates string with date and time according to - * http://xmpp.org/extensions/xep-0082.html - * - * @param value - * @return null if value was null. - */ - public static String serializeDateTime(Date value) { - if (value == null) - return null; - return StringUtils.formatXEP0082Date(value); - } - - /** - * @param value - * @return null if source value was null. - */ - public static String serializeInteger(Integer value) { - if (value == null) - return null; - return value.toString(); - } - - /** - * @param date - * @return null if value was null. - */ - public static String serializeBoolean(Boolean value) { - if (value == null) - return null; - return value.toString(); - } + private SerializerUtils() { + } + + /** + * Returned packet as string with xml. String is ready to be written to the + * stream. + * + * @param instance + * @return + */ + public static String toXml(Instance instance) { + Writer writer = new StringWriter(); + XmlSerializer serializer = Xml.newSerializer(); + try { + serializer.setOutput(writer); + instance.serialize(serializer); + serializer.flush(); + } catch (IOException e) { + return ""; + } + return writer.toString(); + } + + /** + * Serialize container using its element name, namespace and content. + * + * @param serializer + * @param container + * @throws IOException + */ + public static void serialize(XmlSerializer serializer, Container container) + throws IOException { + serializer.setPrefix("", container.getNamespace()); + serializer.startTag(container.getNamespace(), + container.getElementName()); + container.serializeContent(serializer); + serializer.endTag(container.getNamespace(), container.getElementName()); + } + + /** + * Adds inner tag. + * + * @param serializer + * @param elementName + * @throws IOException + */ + public static void addEmtpyTag(XmlSerializer serializer, String elementName) + throws IOException { + serializer.startTag(null, elementName); + serializer.endTag(null, elementName); + } + + /** + * Adds inner tag with text payload. + * + * @param serializer + * @param elementName + * @param innerValue + * @throws IOException + */ + public static void addTextTag(XmlSerializer serializer, String elementName, + String innerValue) throws IOException { + serializer.startTag(null, elementName); + serializer.text(innerValue); + serializer.endTag(null, elementName); + } + + public static void addDateTimeTag(XmlSerializer serializer, + String elementName, Date innerValue) throws IOException { + addTextTag(serializer, elementName, serializeDateTime(innerValue)); + } + + public static void addIntegerTag(XmlSerializer serializer, + String elementName, Integer innerValue) throws IOException { + addTextTag(serializer, elementName, serializeInteger(innerValue)); + } + + public static void addBooleanTag(XmlSerializer serializer, + String elementName, Boolean innerValue) throws IOException { + addTextTag(serializer, elementName, serializeBoolean(innerValue)); + } + + /** + * Sets attribute. + * + * @param serializer + * @param attributeName + * @param value + * @throws IOException + */ + public static void setTextAttribute(XmlSerializer serializer, + String attributeName, String value) throws IOException { + serializer.attribute(null, attributeName, value); + } + + public static void setDateTimeAttribute(XmlSerializer serializer, + String attributeName, Date value) throws IOException { + setTextAttribute(serializer, attributeName, serializeDateTime(value)); + } + + public static void setIntegerAttribute(XmlSerializer serializer, + String attributeName, Integer value) throws IOException { + setTextAttribute(serializer, attributeName, serializeInteger(value)); + } + + public static void setBooleanAttribute(XmlSerializer serializer, + String attributeName, Boolean value) throws IOException { + setTextAttribute(serializer, attributeName, serializeBoolean(value)); + } + + /** + * Creates string with date and time according to + * http://xmpp.org/extensions/xep-0082.html + * + * @param value + * @return null if value was null. + */ + public static String serializeDateTime(Date value) { + if (value == null) + return null; + return StringUtils.formatXEP0082Date(value); + } + + /** + * @param value + * @return null if source value was null. + */ + public static String serializeInteger(Integer value) { + if (value == null) + return null; + return value.toString(); + } + + /** + * @param date + * @return null if value was null. + */ + public static String serializeBoolean(Boolean value) { + if (value == null) + return null; + return value.toString(); + } } diff --git a/app/src/main/java/com/xabber/xmpp/address/Jid.java b/app/src/main/java/com/xabber/xmpp/address/Jid.java index f0155d66f4..83d66cc167 100644 --- a/app/src/main/java/com/xabber/xmpp/address/Jid.java +++ b/app/src/main/java/com/xabber/xmpp/address/Jid.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,66 +20,65 @@ /** * Provides methods to process Jabber Identifier. - * + *

* Warning: Implementation should be review, methods renamed according to * http://xmpp.org/rfcs/rfc6122.html - * + * * @author alexander.ivanov - * */ public class Jid { - private Jid() { - } + private Jid() { + } - /** - * @param user - * @return Lower cased resource or null for null - * argument. - */ - public static String getResource(String user) { - return user == null ? null : StringUtils.parseResource(user - .toLowerCase(Locale.US)); - } + /** + * @param user + * @return Lower cased resource or null for null + * argument. + */ + public static String getResource(String user) { + return user == null ? null : StringUtils.parseResource(user + .toLowerCase(Locale.US)); + } - /** - * @param user - * @return Lower cased server name or null for - * null argument. - */ - public static String getServer(String user) { - return user == null ? null : StringUtils.parseServer(user - .toLowerCase(Locale.US)); - } + /** + * @param user + * @return Lower cased server name or null for + * null argument. + */ + public static String getServer(String user) { + return user == null ? null : StringUtils.parseServer(user + .toLowerCase(Locale.US)); + } - /** - * @param user - * @return Lower cased user name part or null for - * null argument. - */ - public static String getName(String user) { - return user == null ? null : StringUtils.parseName(user - .toLowerCase(Locale.US)); - } + /** + * @param user + * @return Lower cased user name part or null for + * null argument. + */ + public static String getName(String user) { + return user == null ? null : StringUtils.parseName(user + .toLowerCase(Locale.US)); + } - /** - * @param user - * @return Lower cased bare address or null for - * null argument. - */ - public static String getBareAddress(String user) { - return user == null ? null : StringUtils.parseBareAddress(user - .toLowerCase(Locale.US)); - } + /** + * @param user + * @return Lower cased bare address or null for + * null argument. + */ + public static String getBareAddress(String user) { + return user == null ? null : StringUtils.parseBareAddress(user + .toLowerCase(Locale.US)); + } - /** - * Gets lower cased address. - * - * @param user - * @return - */ - public static String getStringPrep(String user) { - return user == null ? null : user.toLowerCase(Locale.US); - } + /** + * Gets lower cased address. + * + * @param user + * @return + */ + public static String getStringPrep(String user) { + return user == null ? null : user.toLowerCase(Locale.US); + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/AbstractChat.java b/app/src/main/java/com/xabber/xmpp/archive/AbstractChat.java index 2d5b55a74c..1ca8c49b36 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/AbstractChat.java +++ b/app/src/main/java/com/xabber/xmpp/archive/AbstractChat.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -25,59 +25,59 @@ public abstract class AbstractChat extends IQ implements PacketExtension { - static final String NAMESPACE = "urn:xmpp:archive"; - - static final String START_ATTRIBUTE = "start"; - static final String WITH_ATTRIBUTE = "with"; - - private Date start; - private String startString; - private String with; - - @Override - public void serializeContent(XmlSerializer serializer) throws IOException { - if (startString != null) - SerializerUtils.setTextAttribute(serializer, START_ATTRIBUTE, - startString); - else if (start != null) - SerializerUtils.setDateTimeAttribute(serializer, START_ATTRIBUTE, - start); - if (with != null) - SerializerUtils.setTextAttribute(serializer, WITH_ATTRIBUTE, with); - } - - @Override - public boolean isValid() { - return true; - } - - @Override - public String getNamespace() { - return NAMESPACE; - } - - public Date getStart() { - return start; - } - - public void setStart(Date start) { - this.start = start; - } - - public String getStartString() { - return startString; - } - - public void setStartString(String startString) { - this.startString = startString; - } - - public String getWith() { - return with; - } - - public void setWith(String with) { - this.with = with; - } + static final String NAMESPACE = "urn:xmpp:archive"; + + static final String START_ATTRIBUTE = "start"; + static final String WITH_ATTRIBUTE = "with"; + + private Date start; + private String startString; + private String with; + + @Override + public void serializeContent(XmlSerializer serializer) throws IOException { + if (startString != null) + SerializerUtils.setTextAttribute(serializer, START_ATTRIBUTE, + startString); + else if (start != null) + SerializerUtils.setDateTimeAttribute(serializer, START_ATTRIBUTE, + start); + if (with != null) + SerializerUtils.setTextAttribute(serializer, WITH_ATTRIBUTE, with); + } + + @Override + public boolean isValid() { + return true; + } + + @Override + public String getNamespace() { + return NAMESPACE; + } + + public Date getStart() { + return start; + } + + public void setStart(Date start) { + this.start = start; + } + + public String getStartString() { + return startString; + } + + public void setStartString(String startString) { + this.startString = startString; + } + + public String getWith() { + return with; + } + + public void setWith(String with) { + this.with = with; + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/AbstractLink.java b/app/src/main/java/com/xabber/xmpp/archive/AbstractLink.java index b6b1ec98a1..04dec6cc1a 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/AbstractLink.java +++ b/app/src/main/java/com/xabber/xmpp/archive/AbstractLink.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -24,50 +24,49 @@ /** * Link inside the {@link Chat}. - * + * * @author alexander.ivanov - * */ public abstract class AbstractLink implements Instance { - static final String START_ATTRIBUTE = "start"; - static final String WITH_ATTRIBUTE = "with"; + static final String START_ATTRIBUTE = "start"; + static final String WITH_ATTRIBUTE = "with"; - private Date start; - private String with; + private Date start; + private String with; - @Override - public boolean isValid() { - return true; - } + @Override + public boolean isValid() { + return true; + } - abstract String getElementName(); + abstract String getElementName(); - @Override - public void serialize(XmlSerializer serializer) throws IOException { - serializer.startTag(null, getElementName()); - if (start != null) - SerializerUtils.setDateTimeAttribute(serializer, START_ATTRIBUTE, - start); - if (with != null) - SerializerUtils.setTextAttribute(serializer, WITH_ATTRIBUTE, with); - serializer.endTag(null, getElementName()); - } + @Override + public void serialize(XmlSerializer serializer) throws IOException { + serializer.startTag(null, getElementName()); + if (start != null) + SerializerUtils.setDateTimeAttribute(serializer, START_ATTRIBUTE, + start); + if (with != null) + SerializerUtils.setTextAttribute(serializer, WITH_ATTRIBUTE, with); + serializer.endTag(null, getElementName()); + } - public Date getStart() { - return start; - } + public Date getStart() { + return start; + } - public void setStart(Date start) { - this.start = start; - } + public void setStart(Date start) { + this.start = start; + } - public String getWith() { - return with; - } + public String getWith() { + return with; + } - public void setWith(String with) { - this.with = with; - } + public void setWith(String with) { + this.with = with; + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/AbstractLinkProvider.java b/app/src/main/java/com/xabber/xmpp/archive/AbstractLinkProvider.java index 53e876223e..c461b074c5 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/AbstractLinkProvider.java +++ b/app/src/main/java/com/xabber/xmpp/archive/AbstractLinkProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,15 +20,15 @@ import com.xabber.xmpp.ProviderUtils; public abstract class AbstractLinkProvider extends - AbstractProvider { + AbstractProvider { - @Override - protected T preProcess(XmlPullParser parser, T instance) { - instance.setStart(ProviderUtils.parseDateTime(parser.getAttributeValue( - null, AbstractLink.START_ATTRIBUTE))); - instance.setWith(parser.getAttributeValue(null, - AbstractLink.WITH_ATTRIBUTE)); - return super.preProcess(parser, instance); - } + @Override + protected T preProcess(XmlPullParser parser, T instance) { + instance.setStart(ProviderUtils.parseDateTime(parser.getAttributeValue( + null, AbstractLink.START_ATTRIBUTE))); + instance.setWith(parser.getAttributeValue(null, + AbstractLink.WITH_ATTRIBUTE)); + return super.preProcess(parser, instance); + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/AbstractMessage.java b/app/src/main/java/com/xabber/xmpp/archive/AbstractMessage.java index bf7812ea9b..5f89b5c9d9 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/AbstractMessage.java +++ b/app/src/main/java/com/xabber/xmpp/archive/AbstractMessage.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -24,90 +24,89 @@ /** * Message item inside the {@link Chat}. - * + * * @author alexander.ivanov - * */ public abstract class AbstractMessage implements Instance { - static final String BODY_NAME = "body"; - - static final String JID_ATTRIBUTE = "jid"; - static final String NAME_ATTRIBUTE = "name"; - static final String SECS_ATTRIBUTE = "secs"; - static final String UTC_ATTRIBUTE = "utc"; - - private String jid; - private String name; - private Integer secs; - private Date utc; - private String body; - - // TODO: bodies - - @Override - public boolean isValid() { - return body != null && (utc != null || secs != null); - } - - abstract String getElementName(); - - @Override - public void serialize(XmlSerializer serializer) throws IOException { - serializer.startTag(null, getElementName()); - if (jid != null) - SerializerUtils.setTextAttribute(serializer, JID_ATTRIBUTE, jid); - if (name != null) - SerializerUtils.setTextAttribute(serializer, NAME_ATTRIBUTE, name); - if (secs != null) - SerializerUtils.setIntegerAttribute(serializer, SECS_ATTRIBUTE, - secs); - if (utc != null) - SerializerUtils - .setDateTimeAttribute(serializer, UTC_ATTRIBUTE, utc); - if (body != null) - SerializerUtils.addTextTag(serializer, BODY_NAME, body); - serializer.endTag(null, getElementName()); - } - - public String getJid() { - return jid; - } - - public void setJid(String jid) { - this.jid = jid; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Integer getSecs() { - return secs; - } - - public void setSecs(Integer secs) { - this.secs = secs; - } - - public Date getUtc() { - return utc; - } - - public void setUtc(Date utc) { - this.utc = utc; - } - - public String getBody() { - return body; - } - - public void setBody(String body) { - this.body = body; - } + static final String BODY_NAME = "body"; + + static final String JID_ATTRIBUTE = "jid"; + static final String NAME_ATTRIBUTE = "name"; + static final String SECS_ATTRIBUTE = "secs"; + static final String UTC_ATTRIBUTE = "utc"; + + private String jid; + private String name; + private Integer secs; + private Date utc; + private String body; + + // TODO: bodies + + @Override + public boolean isValid() { + return body != null && (utc != null || secs != null); + } + + abstract String getElementName(); + + @Override + public void serialize(XmlSerializer serializer) throws IOException { + serializer.startTag(null, getElementName()); + if (jid != null) + SerializerUtils.setTextAttribute(serializer, JID_ATTRIBUTE, jid); + if (name != null) + SerializerUtils.setTextAttribute(serializer, NAME_ATTRIBUTE, name); + if (secs != null) + SerializerUtils.setIntegerAttribute(serializer, SECS_ATTRIBUTE, + secs); + if (utc != null) + SerializerUtils + .setDateTimeAttribute(serializer, UTC_ATTRIBUTE, utc); + if (body != null) + SerializerUtils.addTextTag(serializer, BODY_NAME, body); + serializer.endTag(null, getElementName()); + } + + public String getJid() { + return jid; + } + + public void setJid(String jid) { + this.jid = jid; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getSecs() { + return secs; + } + + public void setSecs(Integer secs) { + this.secs = secs; + } + + public Date getUtc() { + return utc; + } + + public void setUtc(Date utc) { + this.utc = utc; + } + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/AbstractMessageProvider.java b/app/src/main/java/com/xabber/xmpp/archive/AbstractMessageProvider.java index a09eed744d..766b127bee 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/AbstractMessageProvider.java +++ b/app/src/main/java/com/xabber/xmpp/archive/AbstractMessageProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,32 +20,32 @@ import com.xabber.xmpp.ProviderUtils; public abstract class AbstractMessageProvider - extends AbstractProvider { + extends AbstractProvider { - @Override - protected T preProcess(XmlPullParser parser, T instance) { - instance.setJid(parser.getAttributeValue(null, - AbstractMessage.JID_ATTRIBUTE)); - instance.setName(parser.getAttributeValue(null, - AbstractMessage.NAME_ATTRIBUTE)); - instance.setSecs(ProviderUtils.parseInteger(parser.getAttributeValue( - null, AbstractMessage.SECS_ATTRIBUTE))); - instance.setUtc(ProviderUtils.parseDateTime(parser.getAttributeValue( - null, AbstractMessage.UTC_ATTRIBUTE))); - return super.preProcess(parser, instance); - } + @Override + protected T preProcess(XmlPullParser parser, T instance) { + instance.setJid(parser.getAttributeValue(null, + AbstractMessage.JID_ATTRIBUTE)); + instance.setName(parser.getAttributeValue(null, + AbstractMessage.NAME_ATTRIBUTE)); + instance.setSecs(ProviderUtils.parseInteger(parser.getAttributeValue( + null, AbstractMessage.SECS_ATTRIBUTE))); + instance.setUtc(ProviderUtils.parseDateTime(parser.getAttributeValue( + null, AbstractMessage.UTC_ATTRIBUTE))); + return super.preProcess(parser, instance); + } - @Override - protected boolean parseInner(XmlPullParser parser, T instance) - throws Exception { - if (super.parseInner(parser, instance)) - return true; - String name = parser.getName(); - if (AbstractMessage.BODY_NAME.equals(name)) { - instance.setBody(ProviderUtils.parseText(parser)); - } else - return false; - return true; - } + @Override + protected boolean parseInner(XmlPullParser parser, T instance) + throws Exception { + if (super.parseInner(parser, instance)) + return true; + String name = parser.getName(); + if (AbstractMessage.BODY_NAME.equals(name)) { + instance.setBody(ProviderUtils.parseText(parser)); + } else + return false; + return true; + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/AbstractModified.java b/app/src/main/java/com/xabber/xmpp/archive/AbstractModified.java index 9a9863a500..e4e65649ac 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/AbstractModified.java +++ b/app/src/main/java/com/xabber/xmpp/archive/AbstractModified.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -24,80 +24,79 @@ /** * Modification item inside the {@link Modified}. - * + * * @author alexander.ivanov - * */ public abstract class AbstractModified implements CollectionHeader, Instance { - static final String START_ATTRIBUTE = "start"; - static final String VERSION_ATTRIBUTE = "version"; - static final String WITH_ATTRIBUTE = "with"; - - private Date start; - private String startString; - private Integer version; - private String with; - - public AbstractModified() { - } - - abstract String getElementName(); - - @Override - public void serialize(XmlSerializer serializer) throws IOException { - serializer.startTag(null, getElementName()); - if (startString != null) - SerializerUtils.setTextAttribute(serializer, START_ATTRIBUTE, - startString); - else - SerializerUtils.setDateTimeAttribute(serializer, START_ATTRIBUTE, - start); - SerializerUtils.setIntegerAttribute(serializer, VERSION_ATTRIBUTE, - version); - SerializerUtils.setTextAttribute(serializer, WITH_ATTRIBUTE, with); - serializer.endTag(null, getElementName()); - } - - @Override - public boolean isValid() { - return start != null && version != null && with != null; - } - - @Override - public Date getStart() { - return start; - } - - public void setStart(Date start) { - this.start = start; - } - - @Override - public String getStartString() { - return startString; - } - - public void setStartString(String startString) { - this.startString = startString; - } - - @Override - public Integer getVersion() { - return version; - } - - public void setVersion(Integer version) { - this.version = version; - } - - @Override - public String getWith() { - return with; - } - - public void setWith(String with) { - this.with = with; - } + static final String START_ATTRIBUTE = "start"; + static final String VERSION_ATTRIBUTE = "version"; + static final String WITH_ATTRIBUTE = "with"; + + private Date start; + private String startString; + private Integer version; + private String with; + + public AbstractModified() { + } + + abstract String getElementName(); + + @Override + public void serialize(XmlSerializer serializer) throws IOException { + serializer.startTag(null, getElementName()); + if (startString != null) + SerializerUtils.setTextAttribute(serializer, START_ATTRIBUTE, + startString); + else + SerializerUtils.setDateTimeAttribute(serializer, START_ATTRIBUTE, + start); + SerializerUtils.setIntegerAttribute(serializer, VERSION_ATTRIBUTE, + version); + SerializerUtils.setTextAttribute(serializer, WITH_ATTRIBUTE, with); + serializer.endTag(null, getElementName()); + } + + @Override + public boolean isValid() { + return start != null && version != null && with != null; + } + + @Override + public Date getStart() { + return start; + } + + public void setStart(Date start) { + this.start = start; + } + + @Override + public String getStartString() { + return startString; + } + + public void setStartString(String startString) { + this.startString = startString; + } + + @Override + public Integer getVersion() { + return version; + } + + public void setVersion(Integer version) { + this.version = version; + } + + @Override + public String getWith() { + return with; + } + + public void setWith(String with) { + this.with = with; + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/AbstractModifiedProvider.java b/app/src/main/java/com/xabber/xmpp/archive/AbstractModifiedProvider.java index bb5660288d..b6fa1e9f4c 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/AbstractModifiedProvider.java +++ b/app/src/main/java/com/xabber/xmpp/archive/AbstractModifiedProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,19 +20,19 @@ import com.xabber.xmpp.ProviderUtils; public abstract class AbstractModifiedProvider - extends AbstractProvider { + extends AbstractProvider { - @Override - protected T preProcess(XmlPullParser parser, T instance) { - instance.setStart(ProviderUtils.parseDateTime(parser.getAttributeValue( - null, AbstractModified.START_ATTRIBUTE))); - instance.setStartString(parser.getAttributeValue(null, - AbstractModified.START_ATTRIBUTE)); - instance.setVersion(ProviderUtils.parseInteger(parser - .getAttributeValue(null, AbstractModified.VERSION_ATTRIBUTE))); - instance.setWith(parser.getAttributeValue(null, - AbstractModified.WITH_ATTRIBUTE)); - return super.preProcess(parser, instance); - } + @Override + protected T preProcess(XmlPullParser parser, T instance) { + instance.setStart(ProviderUtils.parseDateTime(parser.getAttributeValue( + null, AbstractModified.START_ATTRIBUTE))); + instance.setStartString(parser.getAttributeValue(null, + AbstractModified.START_ATTRIBUTE)); + instance.setVersion(ProviderUtils.parseInteger(parser + .getAttributeValue(null, AbstractModified.VERSION_ATTRIBUTE))); + instance.setWith(parser.getAttributeValue(null, + AbstractModified.WITH_ATTRIBUTE)); + return super.preProcess(parser, instance); + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/AbstractSettings.java b/app/src/main/java/com/xabber/xmpp/archive/AbstractSettings.java index c0da04a797..24cbcce925 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/AbstractSettings.java +++ b/app/src/main/java/com/xabber/xmpp/archive/AbstractSettings.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -23,68 +23,67 @@ /** * Settings item inside the {@link Pref}. - * + * * @author alexander.ivanov - * */ public abstract class AbstractSettings implements Instance { - public static final String EXPIRE_ATTRIBUTE = "expire"; - public static final String OTR_ATTRIBUTE = "otr"; - public static final String SAVE_ATTRIBUTE = "save"; - - private Integer expire; - private OtrMode otr; - private SaveMode save; - - @Override - public boolean isValid() { - return true; - } - - abstract String getElementName(); - - @Override - public void serialize(XmlSerializer serializer) throws IOException { - serializer.startTag(null, getElementName()); - serializeAttributes(serializer); - serializer.endTag(null, getElementName()); - } - - void serializeAttributes(XmlSerializer serializer) throws IOException { - if (expire != null) - SerializerUtils.setIntegerAttribute(serializer, EXPIRE_ATTRIBUTE, - expire); - if (otr != null) - SerializerUtils.setTextAttribute(serializer, OTR_ATTRIBUTE, - otr.toString()); - if (save != null) - SerializerUtils.setTextAttribute(serializer, SAVE_ATTRIBUTE, - save.toString()); - } - - public Integer getExpire() { - return expire; - } - - public void setExpire(Integer expire) { - this.expire = expire; - } - - public OtrMode getOtr() { - return otr; - } - - public void setOtr(OtrMode otr) { - this.otr = otr; - } - - public SaveMode getSave() { - return save; - } - - public void setSave(SaveMode save) { - this.save = save; - } + public static final String EXPIRE_ATTRIBUTE = "expire"; + public static final String OTR_ATTRIBUTE = "otr"; + public static final String SAVE_ATTRIBUTE = "save"; + + private Integer expire; + private OtrMode otr; + private SaveMode save; + + @Override + public boolean isValid() { + return true; + } + + abstract String getElementName(); + + @Override + public void serialize(XmlSerializer serializer) throws IOException { + serializer.startTag(null, getElementName()); + serializeAttributes(serializer); + serializer.endTag(null, getElementName()); + } + + void serializeAttributes(XmlSerializer serializer) throws IOException { + if (expire != null) + SerializerUtils.setIntegerAttribute(serializer, EXPIRE_ATTRIBUTE, + expire); + if (otr != null) + SerializerUtils.setTextAttribute(serializer, OTR_ATTRIBUTE, + otr.toString()); + if (save != null) + SerializerUtils.setTextAttribute(serializer, SAVE_ATTRIBUTE, + save.toString()); + } + + public Integer getExpire() { + return expire; + } + + public void setExpire(Integer expire) { + this.expire = expire; + } + + public OtrMode getOtr() { + return otr; + } + + public void setOtr(OtrMode otr) { + this.otr = otr; + } + + public SaveMode getSave() { + return save; + } + + public void setSave(SaveMode save) { + this.save = save; + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/AbstractSettingsProvider.java b/app/src/main/java/com/xabber/xmpp/archive/AbstractSettingsProvider.java index 07a902cf98..c036430326 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/AbstractSettingsProvider.java +++ b/app/src/main/java/com/xabber/xmpp/archive/AbstractSettingsProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -22,23 +22,23 @@ import com.xabber.xmpp.ProviderUtils; public abstract class AbstractSettingsProvider - extends AbstractProvider { + extends AbstractProvider { - @Override - protected T preProcess(XmlPullParser parser, T instance) { - instance.setExpire(ProviderUtils.parseInteger(parser.getAttributeValue( - null, AbstractSettings.EXPIRE_ATTRIBUTE))); - try { - instance.setOtr(OtrMode.fromString(parser.getAttributeValue(null, - AbstractSettings.OTR_ATTRIBUTE))); - } catch (NoSuchElementException e) { - } - try { - instance.setSave(SaveMode.fromString(parser.getAttributeValue(null, - AbstractSettings.SAVE_ATTRIBUTE))); - } catch (NoSuchElementException e) { - } - return super.preProcess(parser, instance); - } + @Override + protected T preProcess(XmlPullParser parser, T instance) { + instance.setExpire(ProviderUtils.parseInteger(parser.getAttributeValue( + null, AbstractSettings.EXPIRE_ATTRIBUTE))); + try { + instance.setOtr(OtrMode.fromString(parser.getAttributeValue(null, + AbstractSettings.OTR_ATTRIBUTE))); + } catch (NoSuchElementException e) { + } + try { + instance.setSave(SaveMode.fromString(parser.getAttributeValue(null, + AbstractSettings.SAVE_ATTRIBUTE))); + } catch (NoSuchElementException e) { + } + return super.preProcess(parser, instance); + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/Auto.java b/app/src/main/java/com/xabber/xmpp/archive/Auto.java index b8f1007bb8..836d7f3abb 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/Auto.java +++ b/app/src/main/java/com/xabber/xmpp/archive/Auto.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -23,50 +23,49 @@ /** * Packet to change automatic message archive preference. - * + *

* http://xmpp.org/extensions/xep-0136.html - * + * * @author alexander.ivanov - * */ public class Auto extends IQ { - public static final String ELEMENT_NAME = "auto"; - public static final String NAMESPACE = "urn:xmpp:archive"; + public static final String ELEMENT_NAME = "auto"; + public static final String NAMESPACE = "urn:xmpp:archive"; - public static final String SAVE_ATTRIBUTE = "save"; + public static final String SAVE_ATTRIBUTE = "save"; - private boolean save; + private boolean save; - public Auto() { - } + public Auto() { + } - @Override - public void serializeContent(XmlSerializer serializer) throws IOException { - SerializerUtils.setBooleanAttribute(serializer, SAVE_ATTRIBUTE, save); - } + @Override + public void serializeContent(XmlSerializer serializer) throws IOException { + SerializerUtils.setBooleanAttribute(serializer, SAVE_ATTRIBUTE, save); + } - @Override - public boolean isValid() { - return true; - } + @Override + public boolean isValid() { + return true; + } - @Override - public String getElementName() { - return ELEMENT_NAME; - } + @Override + public String getElementName() { + return ELEMENT_NAME; + } - @Override - public String getNamespace() { - return NAMESPACE; - } + @Override + public String getNamespace() { + return NAMESPACE; + } - public boolean isSave() { - return save; - } + public boolean isSave() { + return save; + } - public void setSave(boolean save) { - this.save = save; - } + public void setSave(boolean save) { + this.save = save; + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/Changed.java b/app/src/main/java/com/xabber/xmpp/archive/Changed.java index ba449341a7..1140ffa413 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/Changed.java +++ b/app/src/main/java/com/xabber/xmpp/archive/Changed.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -17,11 +17,11 @@ public class Changed extends AbstractModified { - public static final String ELEMENT_NAME = "changed"; + public static final String ELEMENT_NAME = "changed"; - @Override - String getElementName() { - return ELEMENT_NAME; - } + @Override + String getElementName() { + return ELEMENT_NAME; + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/ChangedProvider.java b/app/src/main/java/com/xabber/xmpp/archive/ChangedProvider.java index 501d8d3b32..da22360e13 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/ChangedProvider.java +++ b/app/src/main/java/com/xabber/xmpp/archive/ChangedProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,18 +18,18 @@ public class ChangedProvider extends AbstractModifiedProvider { - @Override - protected Changed createInstance(XmlPullParser parser) { - return new Changed(); - } + @Override + protected Changed createInstance(XmlPullParser parser) { + return new Changed(); + } - private ChangedProvider() { - } + private ChangedProvider() { + } - private static final ChangedProvider instance = new ChangedProvider(); + private static final ChangedProvider instance = new ChangedProvider(); - public static ChangedProvider getInstance() { - return instance; - } + public static ChangedProvider getInstance() { + return instance; + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/Chat.java b/app/src/main/java/com/xabber/xmpp/archive/Chat.java index 1aa55a5891..36e8038dd5 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/Chat.java +++ b/app/src/main/java/com/xabber/xmpp/archive/Chat.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -26,122 +26,121 @@ /** * Represents message archive collection. - * + *

* http://xmpp.org/extensions/xep-0136.html - * + * * @author alexander.ivanov - * */ public class Chat extends AbstractChat implements CollectionHeader { - static final String ELEMENT_NAME = "chat"; - - static final String SUBJECT_ATTRIBUTE = "subject"; - static final String THREAD_ATTRIBUTE = "thread"; - static final String VERSION_ATTRIBUTE = "version"; - - private String subject; - private String thread; - private Integer version; - private Next next; - private Previous previous; - private final Collection messages; - private Set rsm; - - // TODO: notes - - public Chat() { - messages = new ArrayList(); - } - - @Override - public void serializeContent(XmlSerializer serializer) throws IOException { - super.serializeContent(serializer); - if (subject != null) - SerializerUtils.setTextAttribute(serializer, SUBJECT_ATTRIBUTE, - subject); - if (thread != null) - SerializerUtils.setTextAttribute(serializer, THREAD_ATTRIBUTE, - thread); - if (version != null) - SerializerUtils.setIntegerAttribute(serializer, VERSION_ATTRIBUTE, - version); - if (next != null) - next.serialize(serializer); - if (previous != null) - previous.serialize(serializer); - for (AbstractMessage abstractMessage : messages) - abstractMessage.serialize(serializer); - if (rsm != null) - rsm.serialize(serializer); - } - - @Override - public boolean isValid() { - return super.isValid() && getStart() != null && getWith() != null; - } - - @Override - public String getElementName() { - return ELEMENT_NAME; - } - - public String getSubject() { - return subject; - } - - public void setSubject(String subject) { - this.subject = subject; - } - - public String getThread() { - return thread; - } - - public void setThread(String thread) { - this.thread = thread; - } - - @Override - public Integer getVersion() { - return version; - } - - public void setVersion(Integer version) { - this.version = version; - } - - public Next getNext() { - return next; - } - - public void setNext(Next next) { - this.next = next; - } - - public Previous getPrevious() { - return previous; - } - - public void setPrevious(Previous previous) { - this.previous = previous; - } - - public void addMessage(AbstractMessage value) { - messages.add(value); - } - - public Collection getMessages() { - return Collections.unmodifiableCollection(messages); - } - - public Set getRsm() { - // TODO: Use getExtensions(). - return rsm; - } - - public void setRsm(Set rsm) { - this.rsm = rsm; - } + static final String ELEMENT_NAME = "chat"; + + static final String SUBJECT_ATTRIBUTE = "subject"; + static final String THREAD_ATTRIBUTE = "thread"; + static final String VERSION_ATTRIBUTE = "version"; + + private String subject; + private String thread; + private Integer version; + private Next next; + private Previous previous; + private final Collection messages; + private Set rsm; + + // TODO: notes + + public Chat() { + messages = new ArrayList(); + } + + @Override + public void serializeContent(XmlSerializer serializer) throws IOException { + super.serializeContent(serializer); + if (subject != null) + SerializerUtils.setTextAttribute(serializer, SUBJECT_ATTRIBUTE, + subject); + if (thread != null) + SerializerUtils.setTextAttribute(serializer, THREAD_ATTRIBUTE, + thread); + if (version != null) + SerializerUtils.setIntegerAttribute(serializer, VERSION_ATTRIBUTE, + version); + if (next != null) + next.serialize(serializer); + if (previous != null) + previous.serialize(serializer); + for (AbstractMessage abstractMessage : messages) + abstractMessage.serialize(serializer); + if (rsm != null) + rsm.serialize(serializer); + } + + @Override + public boolean isValid() { + return super.isValid() && getStart() != null && getWith() != null; + } + + @Override + public String getElementName() { + return ELEMENT_NAME; + } + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public String getThread() { + return thread; + } + + public void setThread(String thread) { + this.thread = thread; + } + + @Override + public Integer getVersion() { + return version; + } + + public void setVersion(Integer version) { + this.version = version; + } + + public Next getNext() { + return next; + } + + public void setNext(Next next) { + this.next = next; + } + + public Previous getPrevious() { + return previous; + } + + public void setPrevious(Previous previous) { + this.previous = previous; + } + + public void addMessage(AbstractMessage value) { + messages.add(value); + } + + public Collection getMessages() { + return Collections.unmodifiableCollection(messages); + } + + public Set getRsm() { + // TODO: Use getExtensions(). + return rsm; + } + + public void setRsm(Set rsm) { + this.rsm = rsm; + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/ChatProvider.java b/app/src/main/java/com/xabber/xmpp/archive/ChatProvider.java index 833a3c10ea..66bb608367 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/ChatProvider.java +++ b/app/src/main/java/com/xabber/xmpp/archive/ChatProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -24,75 +24,75 @@ import com.xabber.xmpp.rsm.Set; public class ChatProvider extends AbstractIQProvider implements - PacketExtensionProvider { + PacketExtensionProvider { - @Override - public Chat parseExtension(XmlPullParser parser) throws Exception { - return provideInstance(parser); - } + @Override + public Chat parseExtension(XmlPullParser parser) throws Exception { + return provideInstance(parser); + } - @Override - protected Chat createInstance(XmlPullParser parser) { - return new Chat(); - } + @Override + protected Chat createInstance(XmlPullParser parser) { + return new Chat(); + } - @Override - protected Chat preProcess(XmlPullParser parser, Chat instance) { - instance.setStartString(parser.getAttributeValue(null, - AbstractChat.START_ATTRIBUTE)); - instance.setStart(ProviderUtils.parseDateTime(parser.getAttributeValue( - null, AbstractChat.START_ATTRIBUTE))); - instance.setWith(parser.getAttributeValue(null, - AbstractChat.WITH_ATTRIBUTE)); - instance.setSubject(parser.getAttributeValue(null, - Chat.SUBJECT_ATTRIBUTE)); - instance.setThread(parser - .getAttributeValue(null, Chat.THREAD_ATTRIBUTE)); - instance.setVersion(ProviderUtils.parseInteger(parser - .getAttributeValue(null, Chat.VERSION_ATTRIBUTE))); - return super.preProcess(parser, instance); - } + @Override + protected Chat preProcess(XmlPullParser parser, Chat instance) { + instance.setStartString(parser.getAttributeValue(null, + AbstractChat.START_ATTRIBUTE)); + instance.setStart(ProviderUtils.parseDateTime(parser.getAttributeValue( + null, AbstractChat.START_ATTRIBUTE))); + instance.setWith(parser.getAttributeValue(null, + AbstractChat.WITH_ATTRIBUTE)); + instance.setSubject(parser.getAttributeValue(null, + Chat.SUBJECT_ATTRIBUTE)); + instance.setThread(parser + .getAttributeValue(null, Chat.THREAD_ATTRIBUTE)); + instance.setVersion(ProviderUtils.parseInteger(parser + .getAttributeValue(null, Chat.VERSION_ATTRIBUTE))); + return super.preProcess(parser, instance); + } - @Override - protected boolean parseInner(XmlPullParser parser, Chat instance) - throws Exception { - if (super.parseInner(parser, instance)) - return true; - String name = parser.getName(); - if (To.ELEMENT_NAME.equals(name)) { - To value = ToProvider.getInstance().provideInstance(parser); - if (value.isValid()) - instance.addMessage(value); - } else if (From.ELEMENT_NAME.equals(name)) { - From value = FromProvider.getInstance().provideInstance(parser); - if (value.isValid()) - instance.addMessage(value); - } else if (Next.ELEMENT_NAME.equals(name)) { - Next value = NextProvider.getInstance().provideInstance(parser); - if (value.isValid()) - instance.setNext(value); - } else if (Previous.ELEMENT_NAME.equals(name)) { - Previous value = PreviousProvider.getInstance().provideInstance( - parser); - if (value.isValid()) - instance.setPrevious(value); - } else if (Set.ELEMENT_NAME.equals(name) - && Set.NAMESPACE.equals(parser.getNamespace())) { - PacketExtension packetExtension = PacketParserUtils - .parsePacketExtension(Set.ELEMENT_NAME, Set.NAMESPACE, - parser); - if (packetExtension instanceof Set - && ((Set) packetExtension).isValid()) - instance.setRsm((Set) packetExtension); - } else - return false; - return true; - } + @Override + protected boolean parseInner(XmlPullParser parser, Chat instance) + throws Exception { + if (super.parseInner(parser, instance)) + return true; + String name = parser.getName(); + if (To.ELEMENT_NAME.equals(name)) { + To value = ToProvider.getInstance().provideInstance(parser); + if (value.isValid()) + instance.addMessage(value); + } else if (From.ELEMENT_NAME.equals(name)) { + From value = FromProvider.getInstance().provideInstance(parser); + if (value.isValid()) + instance.addMessage(value); + } else if (Next.ELEMENT_NAME.equals(name)) { + Next value = NextProvider.getInstance().provideInstance(parser); + if (value.isValid()) + instance.setNext(value); + } else if (Previous.ELEMENT_NAME.equals(name)) { + Previous value = PreviousProvider.getInstance().provideInstance( + parser); + if (value.isValid()) + instance.setPrevious(value); + } else if (Set.ELEMENT_NAME.equals(name) + && Set.NAMESPACE.equals(parser.getNamespace())) { + PacketExtension packetExtension = PacketParserUtils + .parsePacketExtension(Set.ELEMENT_NAME, Set.NAMESPACE, + parser); + if (packetExtension instanceof Set + && ((Set) packetExtension).isValid()) + instance.setRsm((Set) packetExtension); + } else + return false; + return true; + } - private static final ChatProvider instance = new ChatProvider(); + private static final ChatProvider instance = new ChatProvider(); - public static ChatProvider getInstance() { - return instance; - } + public static ChatProvider getInstance() { + return instance; + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/CollectionHeader.java b/app/src/main/java/com/xabber/xmpp/archive/CollectionHeader.java index 5c39427106..7fa87c1064 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/CollectionHeader.java +++ b/app/src/main/java/com/xabber/xmpp/archive/CollectionHeader.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,18 +18,17 @@ /** * Common collection's attribute holder. - * + * * @author alexander.ivanov - * */ public interface CollectionHeader { - public Date getStart(); + Date getStart(); - public String getStartString(); + String getStartString(); - public Integer getVersion(); + Integer getVersion(); - public String getWith(); + String getWith(); } diff --git a/app/src/main/java/com/xabber/xmpp/archive/Default.java b/app/src/main/java/com/xabber/xmpp/archive/Default.java index 51f87a7ac2..9e823d66a1 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/Default.java +++ b/app/src/main/java/com/xabber/xmpp/archive/Default.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -22,36 +22,36 @@ public class Default extends AbstractSettings { - public static final String ELEMENT_NAME = "default"; + public static final String ELEMENT_NAME = "default"; - public static final String UNSET_ATTRIBUTE = "unset"; + public static final String UNSET_ATTRIBUTE = "unset"; - private boolean unset; + private boolean unset; - @Override - public boolean isValid() { - return super.isValid() && getOtr() != null && getSave() != null; - } + @Override + public boolean isValid() { + return super.isValid() && getOtr() != null && getSave() != null; + } - @Override - protected String getElementName() { - return ELEMENT_NAME; - } + @Override + protected String getElementName() { + return ELEMENT_NAME; + } - @Override - void serializeAttributes(XmlSerializer serializer) throws IOException { - if (unset) - SerializerUtils.setBooleanAttribute(serializer, UNSET_ATTRIBUTE, - unset); - super.serializeAttributes(serializer); - } + @Override + void serializeAttributes(XmlSerializer serializer) throws IOException { + if (unset) + SerializerUtils.setBooleanAttribute(serializer, UNSET_ATTRIBUTE, + unset); + super.serializeAttributes(serializer); + } - public boolean isUnset() { - return unset; - } + public boolean isUnset() { + return unset; + } - public void setUnset(boolean unset) { - this.unset = unset; - } + public void setUnset(boolean unset) { + this.unset = unset; + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/DefaultProvider.java b/app/src/main/java/com/xabber/xmpp/archive/DefaultProvider.java index 4bd12ab2d0..1f8e292c2c 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/DefaultProvider.java +++ b/app/src/main/java/com/xabber/xmpp/archive/DefaultProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,26 +20,26 @@ public class DefaultProvider extends AbstractSettingsProvider { - @Override - protected Default createInstance(XmlPullParser parser) { - return new Default(); - } + @Override + protected Default createInstance(XmlPullParser parser) { + return new Default(); + } - @Override - protected Default preProcess(XmlPullParser parser, Default instance) { - Boolean unset = ProviderUtils.parseBoolean(parser.getAttributeValue( - null, Default.UNSET_ATTRIBUTE)); - instance.setUnset(unset == null ? false : unset); - return super.preProcess(parser, instance); - } + @Override + protected Default preProcess(XmlPullParser parser, Default instance) { + Boolean unset = ProviderUtils.parseBoolean(parser.getAttributeValue( + null, Default.UNSET_ATTRIBUTE)); + instance.setUnset(unset == null ? false : unset); + return super.preProcess(parser, instance); + } - private DefaultProvider() { - } + private DefaultProvider() { + } - private static final DefaultProvider instance = new DefaultProvider(); + private static final DefaultProvider instance = new DefaultProvider(); - public static DefaultProvider getInstance() { - return instance; - } + public static DefaultProvider getInstance() { + return instance; + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/From.java b/app/src/main/java/com/xabber/xmpp/archive/From.java index 4380abffab..85007c04b5 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/From.java +++ b/app/src/main/java/com/xabber/xmpp/archive/From.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,11 +16,11 @@ public class From extends AbstractMessage { - public static final String ELEMENT_NAME = "from"; + public static final String ELEMENT_NAME = "from"; - @Override - String getElementName() { - return ELEMENT_NAME; - } + @Override + String getElementName() { + return ELEMENT_NAME; + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/FromProvider.java b/app/src/main/java/com/xabber/xmpp/archive/FromProvider.java index edb6ec5d90..6fbc899e18 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/FromProvider.java +++ b/app/src/main/java/com/xabber/xmpp/archive/FromProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,18 +18,18 @@ public class FromProvider extends AbstractMessageProvider { - @Override - protected From createInstance(XmlPullParser parser) { - return new From(); - } + @Override + protected From createInstance(XmlPullParser parser) { + return new From(); + } - private FromProvider() { - } + private FromProvider() { + } - private static final FromProvider instance = new FromProvider(); + private static final FromProvider instance = new FromProvider(); - public static FromProvider getInstance() { - return instance; - } + public static FromProvider getInstance() { + return instance; + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/Item.java b/app/src/main/java/com/xabber/xmpp/archive/Item.java index 1c8e3d22c7..b8d88478b6 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/Item.java +++ b/app/src/main/java/com/xabber/xmpp/archive/Item.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -22,48 +22,48 @@ public class Item extends AbstractSettings { - public static final String ELEMENT_NAME = "item"; + public static final String ELEMENT_NAME = "item"; - public static final String EXACTMATCH_ATTRIBUTE = "exactmatch"; - public static final String JID_ATTRIBUTE = "jid"; + public static final String EXACTMATCH_ATTRIBUTE = "exactmatch"; + public static final String JID_ATTRIBUTE = "jid"; - private Boolean exactmatch; - private String jid; + private Boolean exactmatch; + private String jid; - @Override - public boolean isValid() { - return super.isValid() && jid != null; - } + @Override + public boolean isValid() { + return super.isValid() && jid != null; + } - @Override - String getElementName() { - return ELEMENT_NAME; - } + @Override + String getElementName() { + return ELEMENT_NAME; + } - @Override - protected void serializeAttributes(XmlSerializer serializer) - throws IOException { - if (exactmatch != null) - SerializerUtils.setBooleanAttribute(serializer, - EXACTMATCH_ATTRIBUTE, exactmatch); - SerializerUtils.setTextAttribute(serializer, JID_ATTRIBUTE, jid); - super.serializeAttributes(serializer); - } + @Override + protected void serializeAttributes(XmlSerializer serializer) + throws IOException { + if (exactmatch != null) + SerializerUtils.setBooleanAttribute(serializer, + EXACTMATCH_ATTRIBUTE, exactmatch); + SerializerUtils.setTextAttribute(serializer, JID_ATTRIBUTE, jid); + super.serializeAttributes(serializer); + } - public Boolean getExactmatch() { - return exactmatch; - } + public Boolean getExactmatch() { + return exactmatch; + } - public void setExactmatch(Boolean exactmatch) { - this.exactmatch = exactmatch; - } + public void setExactmatch(Boolean exactmatch) { + this.exactmatch = exactmatch; + } - public String getJid() { - return jid; - } + public String getJid() { + return jid; + } - public void setJid(String jid) { - this.jid = jid; - } + public void setJid(String jid) { + this.jid = jid; + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/ItemProvider.java b/app/src/main/java/com/xabber/xmpp/archive/ItemProvider.java index 01a359bc82..9d20991ade 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/ItemProvider.java +++ b/app/src/main/java/com/xabber/xmpp/archive/ItemProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,27 +20,27 @@ public class ItemProvider extends AbstractSettingsProvider { - @Override - protected Item createInstance(XmlPullParser parser) { - return new Item(); - } + @Override + protected Item createInstance(XmlPullParser parser) { + return new Item(); + } - @Override - protected Item preProcess(XmlPullParser parser, Item instance) { - Boolean exactmatch = ProviderUtils.parseBoolean(parser - .getAttributeValue(null, Item.EXACTMATCH_ATTRIBUTE)); - instance.setExactmatch(exactmatch == null ? false : true); - instance.setJid(parser.getAttributeValue(null, Item.JID_ATTRIBUTE)); - return super.preProcess(parser, instance); - } + @Override + protected Item preProcess(XmlPullParser parser, Item instance) { + Boolean exactmatch = ProviderUtils.parseBoolean(parser + .getAttributeValue(null, Item.EXACTMATCH_ATTRIBUTE)); + instance.setExactmatch(exactmatch == null ? false : true); + instance.setJid(parser.getAttributeValue(null, Item.JID_ATTRIBUTE)); + return super.preProcess(parser, instance); + } - private ItemProvider() { - } + private ItemProvider() { + } - private static final ItemProvider instance = new ItemProvider(); + private static final ItemProvider instance = new ItemProvider(); - public static ItemProvider getInstance() { - return instance; - } + public static ItemProvider getInstance() { + return instance; + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/ItemRemove.java b/app/src/main/java/com/xabber/xmpp/archive/ItemRemove.java index 8d862f8968..c1217ba60f 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/ItemRemove.java +++ b/app/src/main/java/com/xabber/xmpp/archive/ItemRemove.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -25,50 +25,49 @@ /** * Packet to remove items from the message archive preferences. - * + *

* http://xmpp.org/extensions/xep-0136.html - * + * * @author alexander.ivanov - * */ public class ItemRemove extends IQ { - public static final String ELEMENT_NAME = "itemremove"; - public static final String NAMESPACE = "urn:xmpp:archive"; + public static final String ELEMENT_NAME = "itemremove"; + public static final String NAMESPACE = "urn:xmpp:archive"; - private final Collection items; + private final Collection items; - public ItemRemove() { - items = new ArrayList(); - } + public ItemRemove() { + items = new ArrayList(); + } - @Override - public void serializeContent(XmlSerializer serializer) throws IOException { - for (Item item : items) - item.serialize(serializer); - } + @Override + public void serializeContent(XmlSerializer serializer) throws IOException { + for (Item item : items) + item.serialize(serializer); + } - @Override - public boolean isValid() { - return items.size() > 0; - } + @Override + public boolean isValid() { + return items.size() > 0; + } - @Override - public String getElementName() { - return ELEMENT_NAME; - } + @Override + public String getElementName() { + return ELEMENT_NAME; + } - @Override - public String getNamespace() { - return NAMESPACE; - } + @Override + public String getNamespace() { + return NAMESPACE; + } - public void addItem(Item item) { - items.add(item); - } + public void addItem(Item item) { + items.add(item); + } - public Collection getItems() { - return Collections.unmodifiableCollection(items); - } + public Collection getItems() { + return Collections.unmodifiableCollection(items); + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/List.java b/app/src/main/java/com/xabber/xmpp/archive/List.java index 84711c7ded..2c16d20247 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/List.java +++ b/app/src/main/java/com/xabber/xmpp/archive/List.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -28,113 +28,112 @@ /** * List of the chat collection in the message archive. - * + *

* http://xmpp.org/extensions/xep-0136.html - * + * * @author alexander.ivanov - * */ public class List extends IQ { - static final String ELEMENT_NAME = "list"; - static final String NAMESPACE = "urn:xmpp:archive"; - - static final String EXACTMATCH_ATTRIBUTE = "exactmatch"; - static final String START_ATTRIBUTE = "start"; - static final String END_ATTRIBUTE = "end"; - static final String WITH_ATTRIBUTE = "with"; - - private boolean exactmatch; - private Date start; - private Date end; - private String with; - private Set rsm; - private final Collection chats; - - public List() { - chats = new ArrayList(); - } - - @Override - public void serializeContent(XmlSerializer serializer) throws IOException { - if (start != null) - SerializerUtils.setDateTimeAttribute(serializer, START_ATTRIBUTE, - start); - if (end != null) - SerializerUtils - .setDateTimeAttribute(serializer, END_ATTRIBUTE, end); - if (with != null) - SerializerUtils.setTextAttribute(serializer, WITH_ATTRIBUTE, with); - if (exactmatch) - SerializerUtils.setBooleanAttribute(serializer, - EXACTMATCH_ATTRIBUTE, exactmatch); - for (Chat chat : chats) - chat.serialize(serializer); - if (rsm != null) - rsm.serialize(serializer); - } - - @Override - public boolean isValid() { - return true; - } - - @Override - public String getElementName() { - return ELEMENT_NAME; - } - - @Override - public String getNamespace() { - return NAMESPACE; - } - - public boolean isExactmatch() { - return exactmatch; - } - - public void setExactmatch(boolean exactmatch) { - this.exactmatch = exactmatch; - } - - public Date getStart() { - return start; - } - - public void setStart(Date start) { - this.start = start; - } - - public Date getEnd() { - return end; - } - - public void setEnd(Date end) { - this.end = end; - } - - public String getWith() { - return with; - } - - public void setWith(String with) { - this.with = with; - } - - public Set getRsm() { - return rsm; - } - - public void setRsm(Set rsm) { - this.rsm = rsm; - } - - public Collection getChats() { - return Collections.unmodifiableCollection(chats); - } - - public void addChat(Chat value) { - chats.add(value); - } + static final String ELEMENT_NAME = "list"; + static final String NAMESPACE = "urn:xmpp:archive"; + + static final String EXACTMATCH_ATTRIBUTE = "exactmatch"; + static final String START_ATTRIBUTE = "start"; + static final String END_ATTRIBUTE = "end"; + static final String WITH_ATTRIBUTE = "with"; + + private boolean exactmatch; + private Date start; + private Date end; + private String with; + private Set rsm; + private final Collection chats; + + public List() { + chats = new ArrayList(); + } + + @Override + public void serializeContent(XmlSerializer serializer) throws IOException { + if (start != null) + SerializerUtils.setDateTimeAttribute(serializer, START_ATTRIBUTE, + start); + if (end != null) + SerializerUtils + .setDateTimeAttribute(serializer, END_ATTRIBUTE, end); + if (with != null) + SerializerUtils.setTextAttribute(serializer, WITH_ATTRIBUTE, with); + if (exactmatch) + SerializerUtils.setBooleanAttribute(serializer, + EXACTMATCH_ATTRIBUTE, exactmatch); + for (Chat chat : chats) + chat.serialize(serializer); + if (rsm != null) + rsm.serialize(serializer); + } + + @Override + public boolean isValid() { + return true; + } + + @Override + public String getElementName() { + return ELEMENT_NAME; + } + + @Override + public String getNamespace() { + return NAMESPACE; + } + + public boolean isExactmatch() { + return exactmatch; + } + + public void setExactmatch(boolean exactmatch) { + this.exactmatch = exactmatch; + } + + public Date getStart() { + return start; + } + + public void setStart(Date start) { + this.start = start; + } + + public Date getEnd() { + return end; + } + + public void setEnd(Date end) { + this.end = end; + } + + public String getWith() { + return with; + } + + public void setWith(String with) { + this.with = with; + } + + public Set getRsm() { + return rsm; + } + + public void setRsm(Set rsm) { + this.rsm = rsm; + } + + public Collection getChats() { + return Collections.unmodifiableCollection(chats); + } + + public void addChat(Chat value) { + chats.add(value); + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/ListProvider.java b/app/src/main/java/com/xabber/xmpp/archive/ListProvider.java index 1f8707ed95..e468e1b2f0 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/ListProvider.java +++ b/app/src/main/java/com/xabber/xmpp/archive/ListProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -24,45 +24,45 @@ public class ListProvider extends AbstractIQProvider { - @Override - protected List createInstance(XmlPullParser parser) { - return new List(); - } + @Override + protected List createInstance(XmlPullParser parser) { + return new List(); + } - @Override - protected List preProcess(XmlPullParser parser, List instance) { - instance.setStart(ProviderUtils.parseDateTime(parser.getAttributeValue( - null, List.START_ATTRIBUTE))); - instance.setEnd(ProviderUtils.parseDateTime(parser.getAttributeValue( - null, List.END_ATTRIBUTE))); - instance.setWith(parser.getAttributeValue(null, List.WITH_ATTRIBUTE)); - Boolean exactmatch = ProviderUtils.parseBoolean(parser - .getAttributeValue(null, List.EXACTMATCH_ATTRIBUTE)); - instance.setExactmatch(exactmatch == null ? false : true); - return super.preProcess(parser, instance); - } + @Override + protected List preProcess(XmlPullParser parser, List instance) { + instance.setStart(ProviderUtils.parseDateTime(parser.getAttributeValue( + null, List.START_ATTRIBUTE))); + instance.setEnd(ProviderUtils.parseDateTime(parser.getAttributeValue( + null, List.END_ATTRIBUTE))); + instance.setWith(parser.getAttributeValue(null, List.WITH_ATTRIBUTE)); + Boolean exactmatch = ProviderUtils.parseBoolean(parser + .getAttributeValue(null, List.EXACTMATCH_ATTRIBUTE)); + instance.setExactmatch(exactmatch == null ? false : true); + return super.preProcess(parser, instance); + } - @Override - protected boolean parseInner(XmlPullParser parser, List instance) - throws Exception { - if (super.parseInner(parser, instance)) - return true; - String name = parser.getName(); - if (Chat.ELEMENT_NAME.equals(name)) { - Chat value = ChatProvider.getInstance().provideInstance(parser); - if (value.isValid()) - instance.addChat(value); - } else if (Set.ELEMENT_NAME.equals(name) - && Set.NAMESPACE.equals(parser.getNamespace())) { - PacketExtension packetExtension = PacketParserUtils - .parsePacketExtension(Set.ELEMENT_NAME, Set.NAMESPACE, - parser); - if (packetExtension instanceof Set - && ((Set) packetExtension).isValid()) - instance.setRsm((Set) packetExtension); - } else - return false; - return true; - } + @Override + protected boolean parseInner(XmlPullParser parser, List instance) + throws Exception { + if (super.parseInner(parser, instance)) + return true; + String name = parser.getName(); + if (Chat.ELEMENT_NAME.equals(name)) { + Chat value = ChatProvider.getInstance().provideInstance(parser); + if (value.isValid()) + instance.addChat(value); + } else if (Set.ELEMENT_NAME.equals(name) + && Set.NAMESPACE.equals(parser.getNamespace())) { + PacketExtension packetExtension = PacketParserUtils + .parsePacketExtension(Set.ELEMENT_NAME, Set.NAMESPACE, + parser); + if (packetExtension instanceof Set + && ((Set) packetExtension).isValid()) + instance.setRsm((Set) packetExtension); + } else + return false; + return true; + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/Modified.java b/app/src/main/java/com/xabber/xmpp/archive/Modified.java index 78cbc235ee..2ad5567438 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/Modified.java +++ b/app/src/main/java/com/xabber/xmpp/archive/Modified.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -28,75 +28,74 @@ /** * Represents list of modified collection. - * + *

* http://xmpp.org/extensions/xep-0136.html - * + * * @author alexander.ivanov - * */ public class Modified extends IQ { - static final String ELEMENT_NAME = "modified"; - static final String NAMESPACE = "urn:xmpp:archive"; - - static final String START_ATTRIBUTE = "start"; - - private Date start; - private Set rsm; - private final Collection chats; - - public Modified() { - chats = new ArrayList(); - } - - @Override - public void serializeContent(XmlSerializer serializer) throws IOException { - if (start != null) - SerializerUtils.setDateTimeAttribute(serializer, START_ATTRIBUTE, - start); - for (AbstractModified chat : chats) - chat.serialize(serializer); - if (rsm != null) - rsm.serialize(serializer); - } - - @Override - public boolean isValid() { - return true; - } - - @Override - public String getElementName() { - return ELEMENT_NAME; - } - - @Override - public String getNamespace() { - return NAMESPACE; - } - - public Date getStart() { - return start; - } - - public void setStart(Date start) { - this.start = start; - } - - public Set getRsm() { - return rsm; - } - - public void setRsm(Set rsm) { - this.rsm = rsm; - } - - public Collection getChats() { - return Collections.unmodifiableCollection(chats); - } - - public void addModified(AbstractModified value) { - chats.add(value); - } + static final String ELEMENT_NAME = "modified"; + static final String NAMESPACE = "urn:xmpp:archive"; + + static final String START_ATTRIBUTE = "start"; + + private Date start; + private Set rsm; + private final Collection chats; + + public Modified() { + chats = new ArrayList(); + } + + @Override + public void serializeContent(XmlSerializer serializer) throws IOException { + if (start != null) + SerializerUtils.setDateTimeAttribute(serializer, START_ATTRIBUTE, + start); + for (AbstractModified chat : chats) + chat.serialize(serializer); + if (rsm != null) + rsm.serialize(serializer); + } + + @Override + public boolean isValid() { + return true; + } + + @Override + public String getElementName() { + return ELEMENT_NAME; + } + + @Override + public String getNamespace() { + return NAMESPACE; + } + + public Date getStart() { + return start; + } + + public void setStart(Date start) { + this.start = start; + } + + public Set getRsm() { + return rsm; + } + + public void setRsm(Set rsm) { + this.rsm = rsm; + } + + public Collection getChats() { + return Collections.unmodifiableCollection(chats); + } + + public void addModified(AbstractModified value) { + chats.add(value); + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/ModifiedProvider.java b/app/src/main/java/com/xabber/xmpp/archive/ModifiedProvider.java index 31d2943505..2e77c19c64 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/ModifiedProvider.java +++ b/app/src/main/java/com/xabber/xmpp/archive/ModifiedProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -24,45 +24,45 @@ public class ModifiedProvider extends AbstractIQProvider { - @Override - protected Modified createInstance(XmlPullParser parser) { - return new Modified(); - } + @Override + protected Modified createInstance(XmlPullParser parser) { + return new Modified(); + } - @Override - protected Modified preProcess(XmlPullParser parser, Modified instance) { - instance.setStart(ProviderUtils.parseDateTime(parser.getAttributeValue( - null, AbstractChat.START_ATTRIBUTE))); - return super.preProcess(parser, instance); - } + @Override + protected Modified preProcess(XmlPullParser parser, Modified instance) { + instance.setStart(ProviderUtils.parseDateTime(parser.getAttributeValue( + null, AbstractChat.START_ATTRIBUTE))); + return super.preProcess(parser, instance); + } - @Override - protected boolean parseInner(XmlPullParser parser, Modified instance) - throws Exception { - if (super.parseInner(parser, instance)) - return true; - String name = parser.getName(); - if (Changed.ELEMENT_NAME.equals(name)) { - Changed value = ChangedProvider.getInstance().provideInstance( - parser); - if (value.isValid()) - instance.addModified(value); - } else if (Removed.ELEMENT_NAME.equals(name)) { - Removed value = RemovedProvider.getInstance().provideInstance( - parser); - if (value.isValid()) - instance.addModified(value); - } else if (Set.ELEMENT_NAME.equals(name) - && Set.NAMESPACE.equals(parser.getNamespace())) { - PacketExtension packetExtension = PacketParserUtils - .parsePacketExtension(Set.ELEMENT_NAME, Set.NAMESPACE, - parser); - if (packetExtension instanceof Set - && ((Set) packetExtension).isValid()) - instance.setRsm((Set) packetExtension); - } else - return false; - return true; - } + @Override + protected boolean parseInner(XmlPullParser parser, Modified instance) + throws Exception { + if (super.parseInner(parser, instance)) + return true; + String name = parser.getName(); + if (Changed.ELEMENT_NAME.equals(name)) { + Changed value = ChangedProvider.getInstance().provideInstance( + parser); + if (value.isValid()) + instance.addModified(value); + } else if (Removed.ELEMENT_NAME.equals(name)) { + Removed value = RemovedProvider.getInstance().provideInstance( + parser); + if (value.isValid()) + instance.addModified(value); + } else if (Set.ELEMENT_NAME.equals(name) + && Set.NAMESPACE.equals(parser.getNamespace())) { + PacketExtension packetExtension = PacketParserUtils + .parsePacketExtension(Set.ELEMENT_NAME, Set.NAMESPACE, + parser); + if (packetExtension instanceof Set + && ((Set) packetExtension).isValid()) + instance.setRsm((Set) packetExtension); + } else + return false; + return true; + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/Next.java b/app/src/main/java/com/xabber/xmpp/archive/Next.java index 7921bffb36..32a81b359c 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/Next.java +++ b/app/src/main/java/com/xabber/xmpp/archive/Next.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,11 +16,11 @@ public class Next extends AbstractLink { - public static final String ELEMENT_NAME = "next"; + public static final String ELEMENT_NAME = "next"; - @Override - String getElementName() { - return ELEMENT_NAME; - } + @Override + String getElementName() { + return ELEMENT_NAME; + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/NextProvider.java b/app/src/main/java/com/xabber/xmpp/archive/NextProvider.java index c2aa7a5210..fa10cd3e20 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/NextProvider.java +++ b/app/src/main/java/com/xabber/xmpp/archive/NextProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,18 +18,18 @@ public class NextProvider extends AbstractLinkProvider { - @Override - protected Next createInstance(XmlPullParser parser) { - return new Next(); - } + @Override + protected Next createInstance(XmlPullParser parser) { + return new Next(); + } - private NextProvider() { - } + private NextProvider() { + } - private static final NextProvider instance = new NextProvider(); + private static final NextProvider instance = new NextProvider(); - public static NextProvider getInstance() { - return instance; - } + public static NextProvider getInstance() { + return instance; + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/OtrMode.java b/app/src/main/java/com/xabber/xmpp/archive/OtrMode.java index 775a7d7615..a8098a0449 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/OtrMode.java +++ b/app/src/main/java/com/xabber/xmpp/archive/OtrMode.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -23,125 +23,124 @@ /** * User's default setting for OTR Mode. - * + *

* http://xmpp.org/extensions/xep-0136.html#pref-syntax-default-otr - * + * * @author alexander.ivanov - * */ public enum OtrMode { - /** - * The user MUST explicitly approve off-the-record communication. - */ - approve(new LoggingValue[] { LoggingValue.mustnot, LoggingValue.may }), - - /** - * Communications MAY be off the record if requested by another user. - */ - concede(new LoggingValue[] { LoggingValue.may, LoggingValue.mustnot }), - - /** - * Communications MUST NOT be off the record. - */ - forbid(new LoggingValue[] { LoggingValue.may }), - - /** - * Communications SHOULD NOT be off the record even if requested. - */ - oppose(new LoggingValue[] { LoggingValue.may, LoggingValue.mustnot }), - - /** - * Communications SHOULD be off the record if possible. - */ - prefer(new LoggingValue[] { LoggingValue.mustnot, LoggingValue.may }), - - /** - * Communications MUST be off the record. - */ - require(new LoggingValue[] { LoggingValue.mustnot }); - - private final LoggingValue[] loggingValues; - - private OtrMode(LoggingValue[] loggingValues) { - this.loggingValues = loggingValues; - } - - public LoggingValue[] getLoggingValues() { - return loggingValues; - } - - public DisclosureValue getDisclosureValue() { - if (this == require) { - return DisclosureValue.never; - } else if (this == prefer || this == approve || this == concede) { - return DisclosureValue.disabled; - } else if (this == oppose || this == forbid) { - return DisclosureValue.enabled; - } else - throw new IllegalStateException(); - } - - public SecurityValue getSecurityValue() { - if (this == require || this == prefer || this == approve - || this == concede) { - return SecurityValue.c2s; - } else if (this == oppose || this == forbid) { - return SecurityValue.none; - } else - throw new IllegalStateException(); - } - - public boolean acceptLoggingValue(LoggingValue loggingValue) { - for (LoggingValue check : loggingValues) - if (check == loggingValue) - return true; - return false; - } - - public LoggingValue selectLoggingValue(Collection values) { - if (this == require) { - if (values.contains(LoggingValue.mustnot)) - return LoggingValue.mustnot; - else - return null; - } else if (this == prefer) { - if (values.contains(LoggingValue.mustnot)) - return LoggingValue.mustnot; - else if (values.contains(LoggingValue.may)) - return LoggingValue.may; - else - return null; - } else if (this == approve || this == concede) { - if (values.isEmpty()) - return null; - else - return values.iterator().next(); - } else if (this == oppose) { - if (values.contains(LoggingValue.may)) - return LoggingValue.may; - else if (values.contains(LoggingValue.mustnot)) - return LoggingValue.mustnot; - else - return null; - } else if (this == forbid) { - if (values.contains(LoggingValue.may)) - return LoggingValue.may; - else - return null; - } else - throw new IllegalStateException(); - } - - public static OtrMode fromString(String value) - throws NoSuchElementException { - if (value == null) - throw new NoSuchElementException(); - try { - return valueOf(value); - } catch (IllegalArgumentException e) { - throw new NoSuchElementException(); - } - } + /** + * The user MUST explicitly approve off-the-record communication. + */ + approve(new LoggingValue[]{LoggingValue.mustnot, LoggingValue.may}), + + /** + * Communications MAY be off the record if requested by another user. + */ + concede(new LoggingValue[]{LoggingValue.may, LoggingValue.mustnot}), + + /** + * Communications MUST NOT be off the record. + */ + forbid(new LoggingValue[]{LoggingValue.may}), + + /** + * Communications SHOULD NOT be off the record even if requested. + */ + oppose(new LoggingValue[]{LoggingValue.may, LoggingValue.mustnot}), + + /** + * Communications SHOULD be off the record if possible. + */ + prefer(new LoggingValue[]{LoggingValue.mustnot, LoggingValue.may}), + + /** + * Communications MUST be off the record. + */ + require(new LoggingValue[]{LoggingValue.mustnot}); + + private final LoggingValue[] loggingValues; + + OtrMode(LoggingValue[] loggingValues) { + this.loggingValues = loggingValues; + } + + public LoggingValue[] getLoggingValues() { + return loggingValues; + } + + public DisclosureValue getDisclosureValue() { + if (this == require) { + return DisclosureValue.never; + } else if (this == prefer || this == approve || this == concede) { + return DisclosureValue.disabled; + } else if (this == oppose || this == forbid) { + return DisclosureValue.enabled; + } else + throw new IllegalStateException(); + } + + public SecurityValue getSecurityValue() { + if (this == require || this == prefer || this == approve + || this == concede) { + return SecurityValue.c2s; + } else if (this == oppose || this == forbid) { + return SecurityValue.none; + } else + throw new IllegalStateException(); + } + + public boolean acceptLoggingValue(LoggingValue loggingValue) { + for (LoggingValue check : loggingValues) + if (check == loggingValue) + return true; + return false; + } + + public LoggingValue selectLoggingValue(Collection values) { + if (this == require) { + if (values.contains(LoggingValue.mustnot)) + return LoggingValue.mustnot; + else + return null; + } else if (this == prefer) { + if (values.contains(LoggingValue.mustnot)) + return LoggingValue.mustnot; + else if (values.contains(LoggingValue.may)) + return LoggingValue.may; + else + return null; + } else if (this == approve || this == concede) { + if (values.isEmpty()) + return null; + else + return values.iterator().next(); + } else if (this == oppose) { + if (values.contains(LoggingValue.may)) + return LoggingValue.may; + else if (values.contains(LoggingValue.mustnot)) + return LoggingValue.mustnot; + else + return null; + } else if (this == forbid) { + if (values.contains(LoggingValue.may)) + return LoggingValue.may; + else + return null; + } else + throw new IllegalStateException(); + } + + public static OtrMode fromString(String value) + throws NoSuchElementException { + if (value == null) + throw new NoSuchElementException(); + try { + return valueOf(value); + } catch (IllegalArgumentException e) { + throw new NoSuchElementException(); + } + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/Pref.java b/app/src/main/java/com/xabber/xmpp/archive/Pref.java index 56f724f4d3..875f1907ba 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/Pref.java +++ b/app/src/main/java/com/xabber/xmpp/archive/Pref.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -29,115 +29,114 @@ /** * Message archive preferences. - * + *

* http://xmpp.org/extensions/xep-0136.html - * + * * @author alexander.ivanov - * */ public class Pref extends IQ { - public static final String ELEMENT_NAME = "pref"; - public static final String NAMESPACE = "urn:xmpp:archive"; - - public static final String AUTO_NAME = "auto"; - public static final String SAVE_ATTRIBUTE = "save"; - public static final String SCOPE_ATTRIBUTE = "scope"; - - public static final String METHOD_NAME = "method"; - public static final String TYPE_ATTRIBUTE = "type"; - public static final String USE_ATTRIBUTE = "use"; - - private Boolean autoSave; - private ScopeMode autoScope; - private Default defaultItem; - private final Collection items; - private final Collection sessions; - private final Map methods; - - public Pref() { - items = new ArrayList(); - sessions = new ArrayList(); - methods = new HashMap(); - } - - @Override - public void serializeContent(XmlSerializer serializer) throws IOException { - if (autoSave != null) { - serializer.startTag(null, AUTO_NAME); - SerializerUtils.setBooleanAttribute(serializer, SAVE_ATTRIBUTE, - autoSave); - if (autoScope != null) - SerializerUtils.setTextAttribute(serializer, SCOPE_ATTRIBUTE, - autoScope.toString()); - serializer.endTag(null, AUTO_NAME); - } - if (defaultItem != null) - defaultItem.serialize(serializer); - for (Item item : items) - item.serialize(serializer); - for (Session session : sessions) - session.serialize(serializer); - for (Entry entry : methods.entrySet()) { - serializer.startTag(null, METHOD_NAME); - SerializerUtils.setTextAttribute(serializer, TYPE_ATTRIBUTE, entry - .getKey().toString()); - SerializerUtils.setTextAttribute(serializer, USE_ATTRIBUTE, entry - .getValue().toString()); - serializer.endTag(null, METHOD_NAME); - } - } - - @Override - public boolean isValid() { - return true; - } - - @Override - public String getElementName() { - return ELEMENT_NAME; - } - - @Override - public String getNamespace() { - return NAMESPACE; - } - - public void setMethod(TypeMode type, UseMode use) { - methods.put(type, use); - } - - public void setAuto(Boolean save, ScopeMode scope) { - this.autoSave = save; - this.autoScope = scope; - } - - public void setDefault(Default value) { - defaultItem = value; - } - - public void addItem(Item item) { - items.add(item); - } - - public void addSession(Session session) { - sessions.add(session); - } - - public Boolean getAutoSave() { - return autoSave; - } - - public Default getDefault() { - return defaultItem; - } - - public Collection getItems() { - return Collections.unmodifiableCollection(items); - } - - public Collection getSessions() { - return Collections.unmodifiableCollection(sessions); - } + public static final String ELEMENT_NAME = "pref"; + public static final String NAMESPACE = "urn:xmpp:archive"; + + public static final String AUTO_NAME = "auto"; + public static final String SAVE_ATTRIBUTE = "save"; + public static final String SCOPE_ATTRIBUTE = "scope"; + + public static final String METHOD_NAME = "method"; + public static final String TYPE_ATTRIBUTE = "type"; + public static final String USE_ATTRIBUTE = "use"; + + private Boolean autoSave; + private ScopeMode autoScope; + private Default defaultItem; + private final Collection items; + private final Collection sessions; + private final Map methods; + + public Pref() { + items = new ArrayList(); + sessions = new ArrayList(); + methods = new HashMap(); + } + + @Override + public void serializeContent(XmlSerializer serializer) throws IOException { + if (autoSave != null) { + serializer.startTag(null, AUTO_NAME); + SerializerUtils.setBooleanAttribute(serializer, SAVE_ATTRIBUTE, + autoSave); + if (autoScope != null) + SerializerUtils.setTextAttribute(serializer, SCOPE_ATTRIBUTE, + autoScope.toString()); + serializer.endTag(null, AUTO_NAME); + } + if (defaultItem != null) + defaultItem.serialize(serializer); + for (Item item : items) + item.serialize(serializer); + for (Session session : sessions) + session.serialize(serializer); + for (Entry entry : methods.entrySet()) { + serializer.startTag(null, METHOD_NAME); + SerializerUtils.setTextAttribute(serializer, TYPE_ATTRIBUTE, entry + .getKey().toString()); + SerializerUtils.setTextAttribute(serializer, USE_ATTRIBUTE, entry + .getValue().toString()); + serializer.endTag(null, METHOD_NAME); + } + } + + @Override + public boolean isValid() { + return true; + } + + @Override + public String getElementName() { + return ELEMENT_NAME; + } + + @Override + public String getNamespace() { + return NAMESPACE; + } + + public void setMethod(TypeMode type, UseMode use) { + methods.put(type, use); + } + + public void setAuto(Boolean save, ScopeMode scope) { + this.autoSave = save; + this.autoScope = scope; + } + + public void setDefault(Default value) { + defaultItem = value; + } + + public void addItem(Item item) { + items.add(item); + } + + public void addSession(Session session) { + sessions.add(session); + } + + public Boolean getAutoSave() { + return autoSave; + } + + public Default getDefault() { + return defaultItem; + } + + public Collection getItems() { + return Collections.unmodifiableCollection(items); + } + + public Collection getSessions() { + return Collections.unmodifiableCollection(sessions); + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/PrefProvider.java b/app/src/main/java/com/xabber/xmpp/archive/PrefProvider.java index 5901d0137e..aa6a3a342e 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/PrefProvider.java +++ b/app/src/main/java/com/xabber/xmpp/archive/PrefProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -23,63 +23,63 @@ public class PrefProvider extends AbstractIQProvider { - @Override - protected boolean parseInner(XmlPullParser parser, Pref instance) - throws Exception { - if (super.parseInner(parser, instance)) - return true; - String name = parser.getName(); - if (Pref.AUTO_NAME.equals(name)) { - Boolean save = ProviderUtils.parseBoolean(parser.getAttributeValue( - null, Pref.SAVE_ATTRIBUTE)); - if (save != null) { - ScopeMode scope = null; - try { - scope = ScopeMode.fromString(parser.getAttributeValue(null, - Pref.SCOPE_ATTRIBUTE)); - } catch (NoSuchElementException e) { - } - instance.setAuto(save, scope); - } - return false; // Only tag attributes has bee read. - } else if (Default.ELEMENT_NAME.equals(name)) { - Default value = DefaultProvider.getInstance().provideInstance( - parser); - if (value.isValid()) - instance.setDefault(value); - } else if (Item.ELEMENT_NAME.equals(name)) { - Item value = ItemProvider.getInstance().provideInstance(parser); - if (value.isValid()) - instance.addItem(value); - } else if (Session.ELEMENT_NAME.equals(name)) { - Session value = SessionProvider.getInstance().provideInstance( - parser); - if (value.isValid()) - instance.addSession(value); - } else if (Pref.METHOD_NAME.equals(name)) { - TypeMode type = null; - UseMode use = null; - try { - type = TypeMode.fromString(parser.getAttributeValue(null, - Pref.TYPE_ATTRIBUTE)); - } catch (NoSuchElementException e) { - } - try { - use = UseMode.fromString(parser.getAttributeValue(null, - Pref.USE_ATTRIBUTE)); - } catch (NoSuchElementException e) { - } - if (type != null && use != null) - instance.setMethod(type, use); - return false; // Only tag attributes has bee read. - } else - return false; - return true; - } + @Override + protected boolean parseInner(XmlPullParser parser, Pref instance) + throws Exception { + if (super.parseInner(parser, instance)) + return true; + String name = parser.getName(); + if (Pref.AUTO_NAME.equals(name)) { + Boolean save = ProviderUtils.parseBoolean(parser.getAttributeValue( + null, Pref.SAVE_ATTRIBUTE)); + if (save != null) { + ScopeMode scope = null; + try { + scope = ScopeMode.fromString(parser.getAttributeValue(null, + Pref.SCOPE_ATTRIBUTE)); + } catch (NoSuchElementException e) { + } + instance.setAuto(save, scope); + } + return false; // Only tag attributes has bee read. + } else if (Default.ELEMENT_NAME.equals(name)) { + Default value = DefaultProvider.getInstance().provideInstance( + parser); + if (value.isValid()) + instance.setDefault(value); + } else if (Item.ELEMENT_NAME.equals(name)) { + Item value = ItemProvider.getInstance().provideInstance(parser); + if (value.isValid()) + instance.addItem(value); + } else if (Session.ELEMENT_NAME.equals(name)) { + Session value = SessionProvider.getInstance().provideInstance( + parser); + if (value.isValid()) + instance.addSession(value); + } else if (Pref.METHOD_NAME.equals(name)) { + TypeMode type = null; + UseMode use = null; + try { + type = TypeMode.fromString(parser.getAttributeValue(null, + Pref.TYPE_ATTRIBUTE)); + } catch (NoSuchElementException e) { + } + try { + use = UseMode.fromString(parser.getAttributeValue(null, + Pref.USE_ATTRIBUTE)); + } catch (NoSuchElementException e) { + } + if (type != null && use != null) + instance.setMethod(type, use); + return false; // Only tag attributes has bee read. + } else + return false; + return true; + } - @Override - protected Pref createInstance(XmlPullParser parser) { - return new Pref(); - } + @Override + protected Pref createInstance(XmlPullParser parser) { + return new Pref(); + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/Previous.java b/app/src/main/java/com/xabber/xmpp/archive/Previous.java index 1cb5472adf..2572e4e460 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/Previous.java +++ b/app/src/main/java/com/xabber/xmpp/archive/Previous.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,11 +16,11 @@ public class Previous extends AbstractLink { - public static final String ELEMENT_NAME = "previous"; + public static final String ELEMENT_NAME = "previous"; - @Override - String getElementName() { - return ELEMENT_NAME; - } + @Override + String getElementName() { + return ELEMENT_NAME; + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/PreviousProvider.java b/app/src/main/java/com/xabber/xmpp/archive/PreviousProvider.java index dc5ef8fcf3..86e9f3eafb 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/PreviousProvider.java +++ b/app/src/main/java/com/xabber/xmpp/archive/PreviousProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,18 +18,18 @@ public class PreviousProvider extends AbstractLinkProvider { - @Override - protected Previous createInstance(XmlPullParser parser) { - return new Previous(); - } + @Override + protected Previous createInstance(XmlPullParser parser) { + return new Previous(); + } - private PreviousProvider() { - } + private PreviousProvider() { + } - private static final PreviousProvider instance = new PreviousProvider(); + private static final PreviousProvider instance = new PreviousProvider(); - public static PreviousProvider getInstance() { - return instance; - } + public static PreviousProvider getInstance() { + return instance; + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/Remove.java b/app/src/main/java/com/xabber/xmpp/archive/Remove.java index 784f93d845..38f128219f 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/Remove.java +++ b/app/src/main/java/com/xabber/xmpp/archive/Remove.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -23,65 +23,64 @@ /** * Packet to remove collection from the message archive. - * + *

* http://xmpp.org/extensions/xep-0136.html - * + * * @author alexander.ivanov - * */ public class Remove extends AbstractChat { - static final String ELEMENT_NAME = "remove"; - - static final String EXACTMATCH_ATTRIBUTE = "exactmatch"; - static final String END_ATTRIBUTE = "end"; - static final String OPEN_ATTRIBUTE = "open"; - - private boolean exactmatch; - private boolean open; - private Date end; - - @Override - public void serializeContent(XmlSerializer serializer) throws IOException { - super.serializeContent(serializer); - if (end != null) - SerializerUtils - .setDateTimeAttribute(serializer, END_ATTRIBUTE, end); - if (exactmatch) - SerializerUtils.setBooleanAttribute(serializer, - EXACTMATCH_ATTRIBUTE, exactmatch); - if (open) - SerializerUtils.setBooleanAttribute(serializer, OPEN_ATTRIBUTE, - open); - } - - @Override - public String getElementName() { - return ELEMENT_NAME; - } - - public boolean isExactmatch() { - return exactmatch; - } - - public void setExactmatch(boolean exactmatch) { - this.exactmatch = exactmatch; - } - - public boolean isOpen() { - return open; - } - - public void setOpen(boolean open) { - this.open = open; - } - - public Date getEnd() { - return end; - } - - public void setEnd(Date end) { - this.end = end; - } + static final String ELEMENT_NAME = "remove"; + + static final String EXACTMATCH_ATTRIBUTE = "exactmatch"; + static final String END_ATTRIBUTE = "end"; + static final String OPEN_ATTRIBUTE = "open"; + + private boolean exactmatch; + private boolean open; + private Date end; + + @Override + public void serializeContent(XmlSerializer serializer) throws IOException { + super.serializeContent(serializer); + if (end != null) + SerializerUtils + .setDateTimeAttribute(serializer, END_ATTRIBUTE, end); + if (exactmatch) + SerializerUtils.setBooleanAttribute(serializer, + EXACTMATCH_ATTRIBUTE, exactmatch); + if (open) + SerializerUtils.setBooleanAttribute(serializer, OPEN_ATTRIBUTE, + open); + } + + @Override + public String getElementName() { + return ELEMENT_NAME; + } + + public boolean isExactmatch() { + return exactmatch; + } + + public void setExactmatch(boolean exactmatch) { + this.exactmatch = exactmatch; + } + + public boolean isOpen() { + return open; + } + + public void setOpen(boolean open) { + this.open = open; + } + + public Date getEnd() { + return end; + } + + public void setEnd(Date end) { + this.end = end; + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/Removed.java b/app/src/main/java/com/xabber/xmpp/archive/Removed.java index 713c7602f5..4ee6dd308b 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/Removed.java +++ b/app/src/main/java/com/xabber/xmpp/archive/Removed.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,11 +16,11 @@ public class Removed extends AbstractModified { - public static final String ELEMENT_NAME = "removed"; + public static final String ELEMENT_NAME = "removed"; - @Override - String getElementName() { - return ELEMENT_NAME; - } + @Override + String getElementName() { + return ELEMENT_NAME; + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/RemovedProvider.java b/app/src/main/java/com/xabber/xmpp/archive/RemovedProvider.java index d8007c25ee..edbbbb00aa 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/RemovedProvider.java +++ b/app/src/main/java/com/xabber/xmpp/archive/RemovedProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,18 +18,18 @@ public class RemovedProvider extends AbstractModifiedProvider { - @Override - protected Removed createInstance(XmlPullParser parser) { - return new Removed(); - } + @Override + protected Removed createInstance(XmlPullParser parser) { + return new Removed(); + } - private RemovedProvider() { - } + private RemovedProvider() { + } - private static final RemovedProvider instance = new RemovedProvider(); + private static final RemovedProvider instance = new RemovedProvider(); - public static RemovedProvider getInstance() { - return instance; - } + public static RemovedProvider getInstance() { + return instance; + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/Retrieve.java b/app/src/main/java/com/xabber/xmpp/archive/Retrieve.java index 2a15c0e2a5..8a7e642ebf 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/Retrieve.java +++ b/app/src/main/java/com/xabber/xmpp/archive/Retrieve.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -22,36 +22,35 @@ /** * Retrieve message archive collection. - * + *

* http://xmpp.org/extensions/xep-0136.html - * + * * @author alexander.ivanov - * */ public class Retrieve extends AbstractChat { - static final String ELEMENT_NAME = "retrieve"; + static final String ELEMENT_NAME = "retrieve"; - private Set rsm; + private Set rsm; - @Override - public void serializeContent(XmlSerializer serializer) throws IOException { - super.serializeContent(serializer); - if (rsm != null) - rsm.serialize(serializer); - } + @Override + public void serializeContent(XmlSerializer serializer) throws IOException { + super.serializeContent(serializer); + if (rsm != null) + rsm.serialize(serializer); + } - @Override - public String getElementName() { - return ELEMENT_NAME; - } + @Override + public String getElementName() { + return ELEMENT_NAME; + } - public Set getRsm() { - return rsm; - } + public Set getRsm() { + return rsm; + } - public void setRsm(Set rsm) { - this.rsm = rsm; - } + public void setRsm(Set rsm) { + this.rsm = rsm; + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/SaveMode.java b/app/src/main/java/com/xabber/xmpp/archive/SaveMode.java index cd1a0736d1..f770ee8ac0 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/SaveMode.java +++ b/app/src/main/java/com/xabber/xmpp/archive/SaveMode.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,51 +18,50 @@ /** * Save mode as specified in http://xmpp.org/extensions/xep-0136.html - * + * * @author alexander.ivanov - * */ public enum SaveMode { - /** - * The saving entity SHOULD save only elements. - */ - body("body"), + /** + * The saving entity SHOULD save only elements. + */ + body("body"), - /** - * The saving entity MUST save nothing. - */ - fls("false"), + /** + * The saving entity MUST save nothing. + */ + fls("false"), - /** - * The saving entity SHOULD save the full XML content of each - * element. - */ - message("message"), + /** + * The saving entity SHOULD save the full XML content of each + * element. + */ + message("message"), - /** - * The saving entity SHOULD save every byte that passes over the stream in - * either direction. - */ - stream("stream"); + /** + * The saving entity SHOULD save every byte that passes over the stream in + * either direction. + */ + stream("stream"); - private String value; + private String value; - private SaveMode(String name) { - this.value = name; - } + SaveMode(String name) { + this.value = name; + } - @Override - public String toString() { - return value; - } + @Override + public String toString() { + return value; + } - public static SaveMode fromString(String value) - throws NoSuchElementException { - for (SaveMode mode : values()) - if (mode.value.equals(value)) - return mode; - throw new NoSuchElementException(); - } + public static SaveMode fromString(String value) + throws NoSuchElementException { + for (SaveMode mode : values()) + if (mode.value.equals(value)) + return mode; + throw new NoSuchElementException(); + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/ScopeMode.java b/app/src/main/java/com/xabber/xmpp/archive/ScopeMode.java index aa8adc2066..0459b83503 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/ScopeMode.java +++ b/app/src/main/java/com/xabber/xmpp/archive/ScopeMode.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,31 +18,30 @@ /** * Scope for auto element. - * + * * @author alexander.ivanov - * */ public enum ScopeMode { - /** - * The setting will remain for next streams. - */ - global, + /** + * The setting will remain for next streams. + */ + global, - /** - * The setting is true only until the end of the stream. - */ - stream; + /** + * The setting is true only until the end of the stream. + */ + stream; - public static ScopeMode fromString(String value) - throws NoSuchElementException { - if (value == null) - throw new NoSuchElementException(); - try { - return valueOf(value); - } catch (IllegalArgumentException e) { - throw new NoSuchElementException(); - } - } + public static ScopeMode fromString(String value) + throws NoSuchElementException { + if (value == null) + throw new NoSuchElementException(); + try { + return valueOf(value); + } catch (IllegalArgumentException e) { + throw new NoSuchElementException(); + } + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/Session.java b/app/src/main/java/com/xabber/xmpp/archive/Session.java index 4388c6a40f..3540be866d 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/Session.java +++ b/app/src/main/java/com/xabber/xmpp/archive/Session.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -23,62 +23,61 @@ /** * Session settings item inside the {@link Pref}. - * + * * @author alexander.ivanov - * */ public class Session implements Instance { - public static final String ELEMENT_NAME = "session"; - - public static final String TIMEOUT_ATTRIBUTE = "timeout"; - public static final String THREAD_ATTRIBUTE = "thread"; - public static final String SAVE_ATTRIBUTE = "save"; - - private Integer timeout; - private String thread; - private SaveMode save; - - @Override - public boolean isValid() { - return thread != null && save != null; - } - - @Override - public void serialize(XmlSerializer serializer) throws IOException { - serializer.startTag(null, ELEMENT_NAME); - if (timeout != null) - SerializerUtils.setIntegerAttribute(serializer, TIMEOUT_ATTRIBUTE, - timeout); - SerializerUtils.setTextAttribute(serializer, THREAD_ATTRIBUTE, thread); - if (save != null) - SerializerUtils.setTextAttribute(serializer, SAVE_ATTRIBUTE, - save.toString()); - serializer.endTag(null, ELEMENT_NAME); - } - - public Integer getTimeout() { - return timeout; - } - - public void setTimeout(Integer timeout) { - this.timeout = timeout; - } - - public String getThread() { - return thread; - } - - public void setThread(String thread) { - this.thread = thread; - } - - public SaveMode getSave() { - return save; - } - - public void setSave(SaveMode save) { - this.save = save; - } + public static final String ELEMENT_NAME = "session"; + + public static final String TIMEOUT_ATTRIBUTE = "timeout"; + public static final String THREAD_ATTRIBUTE = "thread"; + public static final String SAVE_ATTRIBUTE = "save"; + + private Integer timeout; + private String thread; + private SaveMode save; + + @Override + public boolean isValid() { + return thread != null && save != null; + } + + @Override + public void serialize(XmlSerializer serializer) throws IOException { + serializer.startTag(null, ELEMENT_NAME); + if (timeout != null) + SerializerUtils.setIntegerAttribute(serializer, TIMEOUT_ATTRIBUTE, + timeout); + SerializerUtils.setTextAttribute(serializer, THREAD_ATTRIBUTE, thread); + if (save != null) + SerializerUtils.setTextAttribute(serializer, SAVE_ATTRIBUTE, + save.toString()); + serializer.endTag(null, ELEMENT_NAME); + } + + public Integer getTimeout() { + return timeout; + } + + public void setTimeout(Integer timeout) { + this.timeout = timeout; + } + + public String getThread() { + return thread; + } + + public void setThread(String thread) { + this.thread = thread; + } + + public SaveMode getSave() { + return save; + } + + public void setSave(SaveMode save) { + this.save = save; + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/SessionProvider.java b/app/src/main/java/com/xabber/xmpp/archive/SessionProvider.java index c183815551..919a5af5fc 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/SessionProvider.java +++ b/app/src/main/java/com/xabber/xmpp/archive/SessionProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -23,32 +23,32 @@ public class SessionProvider extends AbstractProvider { - @Override - protected Session createInstance(XmlPullParser parser) { - return new Session(); - } - - @Override - protected Session preProcess(XmlPullParser parser, Session instance) { - instance.setTimeout(ProviderUtils.parseInteger(parser - .getAttributeValue(null, Session.TIMEOUT_ATTRIBUTE))); - instance.setThread(parser.getAttributeValue(null, - Session.THREAD_ATTRIBUTE)); - try { - instance.setSave(SaveMode.fromString(parser.getAttributeValue(null, - Session.SAVE_ATTRIBUTE))); - } catch (NoSuchElementException e) { - } - return super.preProcess(parser, instance); - } - - private SessionProvider() { - } - - private static final SessionProvider instance = new SessionProvider(); - - public static SessionProvider getInstance() { - return instance; - } + @Override + protected Session createInstance(XmlPullParser parser) { + return new Session(); + } + + @Override + protected Session preProcess(XmlPullParser parser, Session instance) { + instance.setTimeout(ProviderUtils.parseInteger(parser + .getAttributeValue(null, Session.TIMEOUT_ATTRIBUTE))); + instance.setThread(parser.getAttributeValue(null, + Session.THREAD_ATTRIBUTE)); + try { + instance.setSave(SaveMode.fromString(parser.getAttributeValue(null, + Session.SAVE_ATTRIBUTE))); + } catch (NoSuchElementException e) { + } + return super.preProcess(parser, instance); + } + + private SessionProvider() { + } + + private static final SessionProvider instance = new SessionProvider(); + + public static SessionProvider getInstance() { + return instance; + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/SessionRemove.java b/app/src/main/java/com/xabber/xmpp/archive/SessionRemove.java index f398855f72..588f649daa 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/SessionRemove.java +++ b/app/src/main/java/com/xabber/xmpp/archive/SessionRemove.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -25,50 +25,49 @@ /** * Packet to remove sessions from the message archive preferences. - * + *

* http://xmpp.org/extensions/xep-0136.html - * + * * @author alexander.ivanov - * */ public class SessionRemove extends IQ { - public static final String ELEMENT_NAME = "sessionremove"; - public static final String NAMESPACE = "urn:xmpp:archive"; + public static final String ELEMENT_NAME = "sessionremove"; + public static final String NAMESPACE = "urn:xmpp:archive"; - private final Collection sessions; + private final Collection sessions; - public SessionRemove() { - sessions = new ArrayList(); - } + public SessionRemove() { + sessions = new ArrayList(); + } - @Override - public void serializeContent(XmlSerializer serializer) throws IOException { - for (Session session : sessions) - session.serialize(serializer); - } + @Override + public void serializeContent(XmlSerializer serializer) throws IOException { + for (Session session : sessions) + session.serialize(serializer); + } - @Override - public boolean isValid() { - return sessions.size() > 0; - } + @Override + public boolean isValid() { + return sessions.size() > 0; + } - @Override - public String getElementName() { - return ELEMENT_NAME; - } + @Override + public String getElementName() { + return ELEMENT_NAME; + } - @Override - public String getNamespace() { - return NAMESPACE; - } + @Override + public String getNamespace() { + return NAMESPACE; + } - public void addSession(Session session) { - sessions.add(session); - } + public void addSession(Session session) { + sessions.add(session); + } - public Collection getSessions() { - return Collections.unmodifiableCollection(sessions); - } + public Collection getSessions() { + return Collections.unmodifiableCollection(sessions); + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/To.java b/app/src/main/java/com/xabber/xmpp/archive/To.java index 9454fd98e1..af80ee38a0 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/To.java +++ b/app/src/main/java/com/xabber/xmpp/archive/To.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,11 +16,11 @@ public class To extends AbstractMessage { - public static final String ELEMENT_NAME = "to"; + public static final String ELEMENT_NAME = "to"; - @Override - String getElementName() { - return ELEMENT_NAME; - } + @Override + String getElementName() { + return ELEMENT_NAME; + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/ToProvider.java b/app/src/main/java/com/xabber/xmpp/archive/ToProvider.java index 829ed48b03..2d1674629e 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/ToProvider.java +++ b/app/src/main/java/com/xabber/xmpp/archive/ToProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,18 +18,18 @@ public class ToProvider extends AbstractMessageProvider { - @Override - protected To createInstance(XmlPullParser parser) { - return new To(); - } + @Override + protected To createInstance(XmlPullParser parser) { + return new To(); + } - private ToProvider() { - } + private ToProvider() { + } - private static final ToProvider instance = new ToProvider(); + private static final ToProvider instance = new ToProvider(); - public static ToProvider getInstance() { - return instance; - } + public static ToProvider getInstance() { + return instance; + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/TypeMode.java b/app/src/main/java/com/xabber/xmpp/archive/TypeMode.java index a603d367c0..a60142cef4 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/TypeMode.java +++ b/app/src/main/java/com/xabber/xmpp/archive/TypeMode.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,38 +18,37 @@ /** * Type of archive method. - * + * * @author alexander.ivanov - * */ public enum TypeMode { - /** - * Preferences for use of automatic archiving on the user's server. - */ - auto, + /** + * Preferences for use of automatic archiving on the user's server. + */ + auto, - /** - * Preferences for use of local archiving to a file or database on the - * user's machine or device. - */ - local, + /** + * Preferences for use of local archiving to a file or database on the + * user's machine or device. + */ + local, - /** - * Preferences for use of manual archiving by the user's client to the - * user's server. - */ - manual; + /** + * Preferences for use of manual archiving by the user's client to the + * user's server. + */ + manual; - public static TypeMode fromString(String value) - throws NoSuchElementException { - if (value == null) - throw new NoSuchElementException(); - try { - return valueOf(value); - } catch (IllegalArgumentException e) { - throw new NoSuchElementException(); - } - } + public static TypeMode fromString(String value) + throws NoSuchElementException { + if (value == null) + throw new NoSuchElementException(); + try { + return valueOf(value); + } catch (IllegalArgumentException e) { + throw new NoSuchElementException(); + } + } } diff --git a/app/src/main/java/com/xabber/xmpp/archive/UseMode.java b/app/src/main/java/com/xabber/xmpp/archive/UseMode.java index 3cca211ce5..7c6c0aa0ca 100644 --- a/app/src/main/java/com/xabber/xmpp/archive/UseMode.java +++ b/app/src/main/java/com/xabber/xmpp/archive/UseMode.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,36 +18,35 @@ /** * Used methods in message archive. - * + * * @author alexander.ivanov - * */ public enum UseMode { - /** - * This method MAY be used if no other methods are available. - */ - concede, + /** + * This method MAY be used if no other methods are available. + */ + concede, - /** - * This method MUST NOT be used. - */ - forbid, + /** + * This method MUST NOT be used. + */ + forbid, - /** - * This method SHOULD be used if available. - */ - prefer; + /** + * This method SHOULD be used if available. + */ + prefer; - public static UseMode fromString(String value) - throws NoSuchElementException { - if (value == null) - throw new NoSuchElementException(); - try { - return valueOf(value); - } catch (IllegalArgumentException e) { - throw new NoSuchElementException(); - } - } + public static UseMode fromString(String value) + throws NoSuchElementException { + if (value == null) + throw new NoSuchElementException(); + try { + return valueOf(value); + } catch (IllegalArgumentException e) { + throw new NoSuchElementException(); + } + } } diff --git a/app/src/main/java/com/xabber/xmpp/attention/Attention.java b/app/src/main/java/com/xabber/xmpp/attention/Attention.java index 10d092d155..cf0fdac14a 100644 --- a/app/src/main/java/com/xabber/xmpp/attention/Attention.java +++ b/app/src/main/java/com/xabber/xmpp/attention/Attention.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -22,32 +22,31 @@ /** * Packet extension for XEP-0224: Attention. - * + * * @author alexander.ivanov - * */ public class Attention extends PacketExtension { - public static final String NAMESPACE = "urn:xmpp:attention:0"; - public static final String ELEMENT_NAME = "attention"; + public static final String NAMESPACE = "urn:xmpp:attention:0"; + public static final String ELEMENT_NAME = "attention"; - @Override - public void serializeContent(XmlSerializer serializer) throws IOException { - } + @Override + public void serializeContent(XmlSerializer serializer) throws IOException { + } - @Override - public boolean isValid() { - return true; - } + @Override + public boolean isValid() { + return true; + } - @Override - public String getElementName() { - return ELEMENT_NAME; - } + @Override + public String getElementName() { + return ELEMENT_NAME; + } - @Override - public String getNamespace() { - return NAMESPACE; - } + @Override + public String getNamespace() { + return NAMESPACE; + } } diff --git a/app/src/main/java/com/xabber/xmpp/attention/AttentionProvider.java b/app/src/main/java/com/xabber/xmpp/attention/AttentionProvider.java index 781137d4b7..547441995c 100644 --- a/app/src/main/java/com/xabber/xmpp/attention/AttentionProvider.java +++ b/app/src/main/java/com/xabber/xmpp/attention/AttentionProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,9 +20,9 @@ public class AttentionProvider extends AbstractExtensionProvider { - @Override - protected Attention createInstance(XmlPullParser parser) { - return new Attention(); - } + @Override + protected Attention createInstance(XmlPullParser parser) { + return new Attention(); + } } diff --git a/app/src/main/java/com/xabber/xmpp/avatar/VCardUpdate.java b/app/src/main/java/com/xabber/xmpp/avatar/VCardUpdate.java index 7687ae9fb3..9304f61269 100644 --- a/app/src/main/java/com/xabber/xmpp/avatar/VCardUpdate.java +++ b/app/src/main/java/com/xabber/xmpp/avatar/VCardUpdate.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -23,80 +23,78 @@ /** * vCard update packet. - * + *

* http://xmpp.org/extensions/xep-0153.html - * + * * @author alexander.ivanov - * */ public class VCardUpdate extends PacketExtension { - public static final String NAMESPACE = "vcard-temp:x:update"; - public static final String ELEMENT_NAME = "x"; - - static final String PHOTO_NAME = "photo"; - - private String photoHash; - - /** - * Create an empty vCard update packet. - * - * Information about photo is not ready to be advertised. - */ - public VCardUpdate() { - photoHash = null; - } - - /** - * @return Whether information about photo is ready to be advertised. - */ - public boolean isPhotoReady() { - return photoHash != null; - } - - /** - * @return Whether photo is advertised to be empty. - */ - public boolean isEmpty() { - return "".equals(photoHash); - } - - /** - * @return Photo's hash. - */ - public String getPhotoHash() { - return photoHash; - } - - /** - * @param hash - * photo's hash value. null if information is not - * ready to be advertised. Empty string no photo is to be - * advertised. - */ - public void setPhotoHash(String hash) { - this.photoHash = hash; - } - - @Override - public boolean isValid() { - return true; - } - - @Override - public String getElementName() { - return ELEMENT_NAME; - } - - @Override - public String getNamespace() { - return NAMESPACE; - } - - @Override - public void serializeContent(XmlSerializer serializer) throws IOException { - if (photoHash != null) - SerializerUtils.addTextTag(serializer, PHOTO_NAME, photoHash); - } + public static final String NAMESPACE = "vcard-temp:x:update"; + public static final String ELEMENT_NAME = "x"; + + static final String PHOTO_NAME = "photo"; + + private String photoHash; + + /** + * Create an empty vCard update packet. + *

+ * Information about photo is not ready to be advertised. + */ + public VCardUpdate() { + photoHash = null; + } + + /** + * @return Whether information about photo is ready to be advertised. + */ + public boolean isPhotoReady() { + return photoHash != null; + } + + /** + * @return Whether photo is advertised to be empty. + */ + public boolean isEmpty() { + return "".equals(photoHash); + } + + /** + * @return Photo's hash. + */ + public String getPhotoHash() { + return photoHash; + } + + /** + * @param hash photo's hash value. null if information is not + * ready to be advertised. Empty string no photo is to be + * advertised. + */ + public void setPhotoHash(String hash) { + this.photoHash = hash; + } + + @Override + public boolean isValid() { + return true; + } + + @Override + public String getElementName() { + return ELEMENT_NAME; + } + + @Override + public String getNamespace() { + return NAMESPACE; + } + + @Override + public void serializeContent(XmlSerializer serializer) throws IOException { + if (photoHash != null) + SerializerUtils.addTextTag(serializer, PHOTO_NAME, photoHash); + } } diff --git a/app/src/main/java/com/xabber/xmpp/avatar/VCardUpdateProvider.java b/app/src/main/java/com/xabber/xmpp/avatar/VCardUpdateProvider.java index ccec4eb8ce..02848aa55e 100644 --- a/app/src/main/java/com/xabber/xmpp/avatar/VCardUpdateProvider.java +++ b/app/src/main/java/com/xabber/xmpp/avatar/VCardUpdateProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -21,22 +21,22 @@ public class VCardUpdateProvider extends AbstractExtensionProvider { - @Override - protected VCardUpdate createInstance(XmlPullParser parser) { - return new VCardUpdate(); - } + @Override + protected VCardUpdate createInstance(XmlPullParser parser) { + return new VCardUpdate(); + } - @Override - protected boolean parseInner(XmlPullParser parser, VCardUpdate instance) - throws Exception { - if (super.parseInner(parser, instance)) - return true; - String name = parser.getName(); - if (VCardUpdate.PHOTO_NAME.equals(name)) - instance.setPhotoHash(ProviderUtils.parseText(parser)); - else - return false; - return true; - } + @Override + protected boolean parseInner(XmlPullParser parser, VCardUpdate instance) + throws Exception { + if (super.parseInner(parser, instance)) + return true; + String name = parser.getName(); + if (VCardUpdate.PHOTO_NAME.equals(name)) + instance.setPhotoHash(ProviderUtils.parseText(parser)); + else + return false; + return true; + } } diff --git a/app/src/main/java/com/xabber/xmpp/carbon/CarbonManager.java b/app/src/main/java/com/xabber/xmpp/carbon/CarbonManager.java new file mode 100644 index 0000000000..7064bcb19f --- /dev/null +++ b/app/src/main/java/com/xabber/xmpp/carbon/CarbonManager.java @@ -0,0 +1,278 @@ +package com.xabber.xmpp.carbon; + +import com.xabber.android.data.Application; +import com.xabber.android.data.LogManager; +import com.xabber.android.data.SettingsManager; +import com.xabber.android.data.SettingsManager.SecurityOtrMode; +import com.xabber.android.data.account.AccountItem; +import com.xabber.android.data.connection.ConnectionItem; +import com.xabber.android.data.connection.OnPacketListener; +import com.xabber.android.data.extension.capability.OnServerInfoReceivedListener; +import com.xabber.android.data.extension.capability.ServerInfoManager; +import com.xabber.android.data.extension.otr.OTRManager; +import com.xabber.android.data.extension.otr.SecurityLevel; +import com.xabber.android.data.message.AbstractChat; +import com.xabber.android.data.message.MessageItem; +import com.xabber.android.data.message.MessageManager; + +import org.jivesoftware.smack.Connection; +import org.jivesoftware.smack.ConnectionCreationListener; +import org.jivesoftware.smack.PacketListener; +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.filter.PacketIDFilter; +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smack.packet.PacketExtension; +import org.jivesoftware.smackx.ServiceDiscoveryManager; + +/** + * Packet extension for XEP-0280: Message Carbons. This class implements + * the manager for registering {@link Carbon} support, enabling and disabling + * message carbons. + *

+ * You should call enableCarbons() before sending your first undirected + * presence. + * + * @author Georg Lukas, Semyon Baranov + */ +public class CarbonManager implements OnServerInfoReceivedListener, OnPacketListener { + public static final String NAMESPACE = "urn:xmpp:carbons:2"; + private final static CarbonManager instance; + + static { + instance = new CarbonManager(); + Application.getInstance().addManager(instance); + + Connection.addConnectionCreationListener(new ConnectionCreationListener() { + @Override + public void connectionCreated(final Connection connection) { + + if (!(connection instanceof XMPPConnection)) { + return; + } + + ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection); + if (sdm != null) { + sdm.addFeature(NAMESPACE); + sdm.addFeature(Forwarded.NAMESPACE); + } + instance.connection = connection; + } + }); + } + + private Connection connection; + private volatile boolean enabled_state = false; + + private CarbonManager() { + } + + public static CarbonManager getInstance() { + + return instance; + } + + /** + * Mark a message as "private", so it will not be carbon-copied. + * + * @param msg Message object to mark private + */ + public static void disableCarbons(Message msg) { + + msg.addExtension(new Private()); + } + + private IQ carbonsEnabledIQ(final boolean new_state) { + + if (!checkConnected()) { + return null; + } + IQ setIQ = new IQ() { + public String getChildElementXML() { + return String.format("<%s xmlns='%s'/>", new_state ? "enable" : "disable", NAMESPACE); + } + }; + setIQ.setType(IQ.Type.SET); + setIQ.setFrom(connection.getUser()); + return setIQ; + } + + private boolean checkConnected() { + + if (connection == null) { + LogManager.exception(this, new Exception("connection is null")); + return false; + } + if (!connection.isConnected()) { + LogManager.exception(this, new Exception("not connected")); + return false; + } + return true; + } + + /** + * Returns true if XMPP Carbons are supported by the server. + * + * @return true if supported + */ + public boolean isSupportedByServer() { + + if (!checkConnected()) { + return false; + } + boolean isCarbonSupported = ServerInfoManager.getInstance().isProtocolSupported(connection.getUser(), NAMESPACE); + return isCarbonSupported; + } + + /** + * Notify server to change the carbons state. This method returns + * immediately and changes the variable when the reply arrives. + *

+ * You should first check for support using isSupportedByServer(). + * + * @param new_state whether carbons should be enabled or disabled + */ + public void sendCarbonsEnabled(final boolean new_state) { + + LogManager.d(this, "sendCarbonsEnabled " + String.valueOf(new_state)); + + if (!checkConnected()) { + return; + } + + IQ setIQ = carbonsEnabledIQ(new_state); + connection.addPacketListener(new PacketListener() { + public void processPacket(Packet packet) { + IQ result = (IQ) packet; + if (result.getType() == IQ.Type.RESULT) { + enabled_state = new_state; + } + connection.removePacketListener(this); + } + }, new PacketIDFilter(setIQ.getPacketID())); + + connection.sendPacket(setIQ); + } + + /** + * Helper method to enable carbons. + */ + public void enableCarbons() { + + sendCarbonsEnabled(true); + } + + /** + * Helper method to disable carbons. + */ + public void disableCarbons() { + + sendCarbonsEnabled(false); + } + + /** + * Check if carbons are enabled on this connection. + */ + public boolean getCarbonsEnabled() { + + return enabled_state; + } + + @Override + public void onPacket(ConnectionItem connection, String bareAddress, + Packet packet) { + + if (!(connection instanceof AccountItem)) { + return; + } + if (!SettingsManager.connectionUseCarbons()) { + return; + } + final String user = packet.getFrom(); + if (user == null) + return; + if (!(packet instanceof Message)) + return; + final Message message = (Message) packet; + if (!getCarbonsEnabled()) { + return; + } + PacketExtension carbonExtension = null; + Direction dir = null; + for (PacketExtension packetExtension : message.getExtensions()) { + if (packetExtension instanceof Received) { + carbonExtension = packetExtension; + dir = Direction.received; + } else if (packetExtension instanceof Sent) { + carbonExtension = packetExtension; + dir = Direction.sent; + } else { + continue; + } + } + if (carbonExtension == null) { + return; + } + Forwarded forwarded = null; + if (dir == Direction.sent) { + Sent carbon = (Sent) carbonExtension; + forwarded = carbon.getForwarded(); + } else { + Received carbon = (Received) carbonExtension; + forwarded = carbon.getForwarded(); + } + if (forwarded == null) { + return; + } + Message forwardedMsg = (Message) forwarded.getForwardedPacket(); + MessageManager.getInstance().displayForwardedMessage(connection, forwardedMsg, dir); + + } + + @Override + public void onServerInfoReceived(ConnectionItem connection) { + + if (isSupportedByServer()) { + onUseCarbonsSettingsChanged(); + } + } + + /** + * Sends the new state of message carbons to the server + * when this setting has been changed + */ + public void onUseCarbonsSettingsChanged() { + + sendCarbonsEnabled(SettingsManager.connectionUseCarbons()); + } + + /** + * Update outgoing message before sending. + * Marks the message as non-carbon-copied in the following cases: + * - Message Carbons is enabled and OTR mode is enabled. + * - Message Carbons is enabled and OTR security level != plain. + * + * @param abstractChat + * @param message the Message to be sent + * @param messageItem + */ + public void updateOutgoingMessage(AbstractChat abstractChat, Message message, MessageItem messageItem) { + + if (!SettingsManager.connectionUseCarbons()) { + return; + } + if (SettingsManager.securityOtrMode() == SecurityOtrMode.disabled) { + return; + } + if (OTRManager.getInstance().getSecurityLevel(abstractChat.getAccount(), + abstractChat.getUser()) != SecurityLevel.plain) { + return; + } + message.addExtension(new Private()); + } + + public enum Direction { + sent, received + } +} \ No newline at end of file diff --git a/app/src/main/java/com/xabber/xmpp/carbon/Forwarded.java b/app/src/main/java/com/xabber/xmpp/carbon/Forwarded.java new file mode 100644 index 0000000000..8d0c53e0b5 --- /dev/null +++ b/app/src/main/java/com/xabber/xmpp/carbon/Forwarded.java @@ -0,0 +1,85 @@ +package com.xabber.xmpp.carbon; + +import java.io.IOException; + +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smackx.packet.DelayInfo; +import org.xmlpull.v1.XmlSerializer; + +import com.xabber.xmpp.Container; +import com.xabber.xmpp.PacketExtension; + +/** + * Packet extension for XEP-0297: Stanza Forwarding. This class implements + * the packet extension to parse + * forwarded messages from a packet. The extension + * XEP-0297 is + * a prerequisite for XEP-0280 (Message Carbons). + * + * @author Semyon Baranov + */ +public class Forwarded extends PacketExtension implements Container { + + public static final String NAMESPACE = "urn:xmpp:forward:0"; + public static final String ELEMENT_NAME = "forwarded"; + + private DelayInfo delay; + private Packet forwardedPacket; + + public Forwarded() { + } + + /** + * Creates a new Forwarded packet extension. + * + * @param delay an optional {@link DelayInfo} timestamp of the packet. + * @param fwdPacket the packet that is forwarded (required). + */ + public Forwarded(DelayInfo delay, Packet fwdPacket) { + this(); + this.delay = delay; + this.forwardedPacket = fwdPacket; + } + + @Override + public String getElementName() { + return ELEMENT_NAME; + } + + @Override + public String getNamespace() { + return NAMESPACE; + } + + @Override + public boolean isValid() { + + return forwardedPacket != null; + } + + @Override + public void serialize(XmlSerializer serializer) throws IOException { + } + + @Override + public void serializeContent(XmlSerializer serializer) throws IOException { + } + + /** + * get the packet forwarded by this stanza. + * + * @return the {@link Packet} instance (typically a message) that was forwarded. + */ + public Packet getForwardedPacket() { + return forwardedPacket; + } + + /** + * get the timestamp of the forwarded packet. + * + * @return the {@link DelayInfo} representing the time when the original packet was sent. May be null. + */ + public DelayInfo getDelayInfo() { + return delay; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/xabber/xmpp/carbon/ForwardedProvider.java b/app/src/main/java/com/xabber/xmpp/carbon/ForwardedProvider.java new file mode 100644 index 0000000000..87157f9a0d --- /dev/null +++ b/app/src/main/java/com/xabber/xmpp/carbon/ForwardedProvider.java @@ -0,0 +1,61 @@ +package com.xabber.xmpp.carbon; + +import java.io.IOException; + +import org.jivesoftware.smack.packet.Packet; +import org.jivesoftware.smack.provider.PacketExtensionProvider; +import org.jivesoftware.smack.util.PacketParserUtils; +import org.jivesoftware.smackx.packet.DelayInfo; +import org.jivesoftware.smackx.provider.DelayInfoProvider; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import com.xabber.android.data.LogManager; +import com.xabber.xmpp.AbstractExtensionProvider; + +/** + * Packet extension for XEP-0297: Stanza Forwarding. This class implements + * a {@link PacketExtensionProvider} to parse forwarded messages from a packet. + * The extension XEP-0297 + * is a prerequisite for XEP-0280 (Message Carbons). + * + *

The {@link ForwardedProvider} must be registered in the + * smack.providers file for the element forwarded with + * namespace urn:xmpp:forwarded:0

to be used. + * + * @author Semyon Baranov + */ +public class ForwardedProvider extends AbstractExtensionProvider { + + DelayInfoProvider dip = new DelayInfoProvider(); + + @Override + protected Forwarded createInstance(XmlPullParser parser) { + + return new Forwarded(null, null); + } + + @Override + public Forwarded parseExtension(XmlPullParser parser) throws Exception { + DelayInfoProvider dip = new DelayInfoProvider(); + DelayInfo di = null; + Packet packet = null; + + boolean done = false; + while (!done) { + int eventType = parser.next(); + if (eventType == XmlPullParser.START_TAG) { + if (parser.getName().equals("delay")) + di = (DelayInfo)dip.parseExtension(parser); + else if (parser.getName().equals("message")) + packet = PacketParserUtils.parseMessage(parser); + else throw new Exception("Unsupported forwarded packet type: " + parser.getName()); + } + else if (eventType == XmlPullParser.END_TAG && parser.getName().equals(Forwarded.ELEMENT_NAME)) + done = true; + } + if (packet == null) + throw new Exception("forwarded extension must contain a packet"); + return new Forwarded(di, packet); + } +} diff --git a/app/src/main/java/com/xabber/xmpp/carbon/Private.java b/app/src/main/java/com/xabber/xmpp/carbon/Private.java new file mode 100644 index 0000000000..b2daa531d2 --- /dev/null +++ b/app/src/main/java/com/xabber/xmpp/carbon/Private.java @@ -0,0 +1,42 @@ +package com.xabber.xmpp.carbon; + +import java.io.IOException; + +import org.xmlpull.v1.XmlSerializer; + +import com.xabber.xmpp.PacketExtension; + +/** + * Packet extension for XEP-0280: Message Carbons. This class implements + * the packet extension to exclude a from being forwarded + * to other Carbons-enabled resources. + * The extension XEP-0280 + * is meant to synchronize a message flow to multiple presences of a user. + * + * @author Semyon Baranov + */ +public class Private extends PacketExtension { + + public static final String ELEMENT_NAME = "private"; + public static final String NAMESPACE = "urn:xmpp:carbons:2"; + + @Override + public String getElementName() { + return ELEMENT_NAME; + } + + @Override + public String getNamespace() { + return NAMESPACE; + } + + @Override + public void serializeContent(XmlSerializer serializer) throws IOException { + } + + @Override + public boolean isValid() { + return true; + } + +} diff --git a/app/src/main/java/com/xabber/xmpp/carbon/PrivateProvider.java b/app/src/main/java/com/xabber/xmpp/carbon/PrivateProvider.java new file mode 100644 index 0000000000..b5c44d0814 --- /dev/null +++ b/app/src/main/java/com/xabber/xmpp/carbon/PrivateProvider.java @@ -0,0 +1,23 @@ +package com.xabber.xmpp.carbon; + +import org.jivesoftware.smack.provider.PacketExtensionProvider; +import org.xmlpull.v1.XmlPullParser; + +import com.xabber.xmpp.AbstractExtensionProvider; + +/** + * Packet extension for XEP-0280: Message Carbons. This class implements + * a {@link PacketExtensionProvider} to exclude a from being + * forwarded to other Carbons-enabled resources. + * The extension XEP-0280 + * is meant to synchronize a message flow to multiple presences of a user. + * + * @author Semyon Baranov + */ +public class PrivateProvider extends AbstractExtensionProvider { + + @Override + protected Private createInstance(XmlPullParser parser) { + return new Private(); + } +} diff --git a/app/src/main/java/com/xabber/xmpp/carbon/Received.java b/app/src/main/java/com/xabber/xmpp/carbon/Received.java new file mode 100644 index 0000000000..269dab506e --- /dev/null +++ b/app/src/main/java/com/xabber/xmpp/carbon/Received.java @@ -0,0 +1,62 @@ +package com.xabber.xmpp.carbon; + +import java.io.IOException; + +import org.xmlpull.v1.XmlSerializer; + +import com.xabber.xmpp.PacketExtension; + +/** + * Packet extension for XEP-0280: Message Carbons. This class implements + * the packet extension to parse a received message from a packet. + * The extension XEP-0280 + * is meant to synchronize a message flow to multiple presences of a user. + * + * @author Semyon Baranov + */ +public class Received extends PacketExtension { + public static final String ELEMENT_NAME = "received"; + public static final String NAMESPACE = "urn:xmpp:carbons:2"; + private Forwarded fwd; + + public Received() { + } + + @Override + public String getElementName() { + return ELEMENT_NAME; + } + + @Override + public String getNamespace() { + return NAMESPACE; + } + + @Override + public void serializeContent(XmlSerializer serializer) throws IOException { + } + + @Override + public boolean isValid() { + return fwd != null; + } + + /** + * Gets the forwarded packet or null if the forwarded has not been set. + * + * @return the {@link Forwarded} message contained in this Carbon. + */ + public Forwarded getForwarded() { + + return fwd; + } + + /** + * Sets the forwarded packet. + * @param fwd - the {@link Forwarded} message contained in this Carbon. + */ + public void setForwarded(Forwarded fwd) { + + this.fwd = fwd; + } +} diff --git a/app/src/main/java/com/xabber/xmpp/carbon/ReceivedProvider.java b/app/src/main/java/com/xabber/xmpp/carbon/ReceivedProvider.java new file mode 100644 index 0000000000..0e72a8cd4d --- /dev/null +++ b/app/src/main/java/com/xabber/xmpp/carbon/ReceivedProvider.java @@ -0,0 +1,45 @@ +package com.xabber.xmpp.carbon; + +import org.jivesoftware.smack.provider.PacketExtensionProvider; +import org.jivesoftware.smack.util.PacketParserUtils; +import org.xmlpull.v1.XmlPullParser; + +import com.xabber.android.data.LogManager; +import com.xabber.xmpp.AbstractExtensionProvider; + +/** + * Packet extension for XEP-0280: Message Carbons. This class implements + * a {@link PacketExtensionProvider} to parse a received message from a packet. + * The extension XEP-0280 + * is meant to synchronize a message flow to multiple presences of a user. + * + * @author Semyon Baranov + */ +public class ReceivedProvider extends AbstractExtensionProvider { + @Override + protected Received createInstance(XmlPullParser parser) { + + return new Received(); + } + + @Override + protected boolean parseInner(XmlPullParser parser, Received instance) + throws Exception { + + Forwarded forwarded = null; + if (parser.getName().equals(Forwarded.ELEMENT_NAME)) { + try { + forwarded = (Forwarded) PacketParserUtils.parsePacketExtension(Forwarded.ELEMENT_NAME, Forwarded.NAMESPACE, parser); + } catch (Exception e) { + LogManager.exception(this, e); + } + } + + if (forwarded == null) { + LogManager.exception(this, new Exception("received extension must contain a forwarded extension")); + return false; + } + instance.setForwarded(forwarded); + return true; + } +} diff --git a/app/src/main/java/com/xabber/xmpp/carbon/Sent.java b/app/src/main/java/com/xabber/xmpp/carbon/Sent.java new file mode 100644 index 0000000000..f5d34ca8dd --- /dev/null +++ b/app/src/main/java/com/xabber/xmpp/carbon/Sent.java @@ -0,0 +1,63 @@ +package com.xabber.xmpp.carbon; + +import java.io.IOException; + +import org.xmlpull.v1.XmlSerializer; + +import com.xabber.xmpp.PacketExtension; + +/** + * Packet extension for XEP-0280: Message Carbons. This class implements + * the packet extension to parse a sent message from a packet. + * The extension XEP-0280 + * is meant to synchronize a message flow to multiple presences of a user. + * + * @author Semyon Baranov + */ +public class Sent extends PacketExtension { + public static final String ELEMENT_NAME = "sent"; + public static final String NAMESPACE = "urn:xmpp:carbons:2"; + private Forwarded fwd; + + public Sent() { + + } + + @Override + public String getElementName() { + return ELEMENT_NAME; + } + + @Override + public String getNamespace() { + return NAMESPACE; + } + + @Override + public void serializeContent(XmlSerializer serializer) throws IOException { + } + + @Override + public boolean isValid() { + return fwd != null; + } + + /** + * Gets the forwarded packet or null if the forwarded has not been set. + * + * @return the {@link Forwarded} message contained in this Carbon. + */ + public Forwarded getForwarded() { + + return fwd; + } + + /** + * Sets the forwarded packet. + * @param fwd - the {@link Forwarded} message contained in this Carbon. + */ + public void setForwarded(Forwarded fwd) { + + this.fwd = fwd; + } +} diff --git a/app/src/main/java/com/xabber/xmpp/carbon/SentProvider.java b/app/src/main/java/com/xabber/xmpp/carbon/SentProvider.java new file mode 100644 index 0000000000..ad49dbcaab --- /dev/null +++ b/app/src/main/java/com/xabber/xmpp/carbon/SentProvider.java @@ -0,0 +1,47 @@ +package com.xabber.xmpp.carbon; + +import org.jivesoftware.smack.provider.PacketExtensionProvider; +import org.jivesoftware.smack.util.PacketParserUtils; +import org.xmlpull.v1.XmlPullParser; + +import com.xabber.android.data.LogManager; +import com.xabber.xmpp.AbstractExtensionProvider; + +/** + * Packet extension for XEP-0280: Message Carbons. This class implements + * a {@link PacketExtensionProvider} to parse a sent message from a packet. + * The extension XEP-0280 + * is meant to synchronize a message flow to multiple presences of a user. + * + * @author Semyon Baranov + */ +public class SentProvider extends AbstractExtensionProvider { + + @Override + protected Sent createInstance(XmlPullParser parser) { + + return new Sent(); + } + + @Override + protected boolean parseInner(XmlPullParser parser, Sent instance) + throws Exception { + + Forwarded forwarded = null; + + if (parser.getName().equals(Forwarded.ELEMENT_NAME)) { + try { + forwarded = (Forwarded) PacketParserUtils.parsePacketExtension(Forwarded.ELEMENT_NAME, Forwarded.NAMESPACE, parser); + } catch (Exception e) { + LogManager.exception(this, e); + } + } + + if (forwarded == null) { + LogManager.exception(this, new Exception("sent extension must contain a forwarded extension")); + return false; + } + instance.setForwarded(forwarded); + return true; + } +} diff --git a/app/src/main/java/com/xabber/xmpp/delay/Delay.java b/app/src/main/java/com/xabber/xmpp/delay/Delay.java index c98a54edbe..01bfc5f874 100644 --- a/app/src/main/java/com/xabber/xmpp/delay/Delay.java +++ b/app/src/main/java/com/xabber/xmpp/delay/Delay.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -24,48 +24,47 @@ /** * Helper class to get delay information. - * + * * @author alexander.ivanov - * */ public class Delay { - private Delay() { - } + private Delay() { + } - /** - * @param packet - * @return Delay value from packet. null if no delay is - * specified. - */ - public static Date getDelay(Packet packet) { - DelayInformation delay = (DelayInformation) packet.getExtension( - "delay", "urn:xmpp:delay"); - // If there was no delay based on XEP-0203, then try XEP-0091 for - // backward compatibility - if (delay == null) { - delay = (DelayInformation) packet.getExtension("x", - "jabber:x:delay"); - } - if (delay == null) - return null; - else - return delay.getStamp(); - } + /** + * @param packet + * @return Delay value from packet. null if no delay is + * specified. + */ + public static Date getDelay(Packet packet) { + DelayInformation delay = (DelayInformation) packet.getExtension( + "delay", "urn:xmpp:delay"); + // If there was no delay based on XEP-0203, then try XEP-0091 for + // backward compatibility + if (delay == null) { + delay = (DelayInformation) packet.getExtension("x", + "jabber:x:delay"); + } + if (delay == null) + return null; + else + return delay.getStamp(); + } - /** - * @param server - * @param packet - * @return Whether message was delayed by server. - */ - public static boolean isOfflineMessage(String server, Packet packet) { - for (PacketExtension extension : packet.getExtensions()) - if (extension instanceof DelayInformation) { - String from = ((DelayInformation) extension).getFrom(); - if (server.equals(Jid.getStringPrep(from))) - return true; - } - return false; - } + /** + * @param server + * @param packet + * @return Whether message was delayed by server. + */ + public static boolean isOfflineMessage(String server, Packet packet) { + for (PacketExtension extension : packet.getExtensions()) + if (extension instanceof DelayInformation) { + String from = ((DelayInformation) extension).getFrom(); + if (server.equals(Jid.getStringPrep(from))) + return true; + } + return false; + } } diff --git a/app/src/main/java/com/xabber/xmpp/form/DataFormType.java b/app/src/main/java/com/xabber/xmpp/form/DataFormType.java index a634416779..7a71c1bf2d 100644 --- a/app/src/main/java/com/xabber/xmpp/form/DataFormType.java +++ b/app/src/main/java/com/xabber/xmpp/form/DataFormType.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,31 +18,30 @@ /** * Form type for the Date Forms. - * + *

* http://xmpp.org/extensions/xep-0004.html#protocol-formtypes - * + * * @author alexander.ivanov - * */ public enum DataFormType { - cancel, + cancel, - form, + form, - result, + result, - submit; + submit; - public static DataFormType fromString(String value) - throws NoSuchElementException { - if (value == null) - throw new NoSuchElementException(); - try { - return valueOf(value); - } catch (IllegalArgumentException e) { - throw new NoSuchElementException(); - } - } + public static DataFormType fromString(String value) + throws NoSuchElementException { + if (value == null) + throw new NoSuchElementException(); + try { + return valueOf(value); + } catch (IllegalArgumentException e) { + throw new NoSuchElementException(); + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/xmpp/muc/Affiliation.java b/app/src/main/java/com/xabber/xmpp/muc/Affiliation.java index ba4c3aee25..fb604c864f 100644 --- a/app/src/main/java/com/xabber/xmpp/muc/Affiliation.java +++ b/app/src/main/java/com/xabber/xmpp/muc/Affiliation.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,33 +18,32 @@ /** * Affiliation in the room. - * + *

* http://xmpp.org/extensions/xep-0045.html#affil - * + * * @author alexander.ivanov - * */ public enum Affiliation { - none, + none, - member, + member, - admin, + admin, - owner, + owner, - outcast; + outcast; - public static Affiliation fromString(String value) - throws NoSuchElementException { - if (value == null) - throw new NoSuchElementException(); - try { - return valueOf(value); - } catch (IllegalArgumentException e) { - throw new NoSuchElementException(); - } - } + public static Affiliation fromString(String value) + throws NoSuchElementException { + if (value == null) + throw new NoSuchElementException(); + try { + return valueOf(value); + } catch (IllegalArgumentException e) { + throw new NoSuchElementException(); + } + } } diff --git a/app/src/main/java/com/xabber/xmpp/muc/MUC.java b/app/src/main/java/com/xabber/xmpp/muc/MUC.java index 077496dfae..9268dd6614 100644 --- a/app/src/main/java/com/xabber/xmpp/muc/MUC.java +++ b/app/src/main/java/com/xabber/xmpp/muc/MUC.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,29 +20,27 @@ /** * Helper class to get MUC information. - * + * * @author alexander.ivanov - * */ public class MUC { - private MUC() { - } + private MUC() { + } - /** - * Returns the MUCUser packet extension included in the packet or - * null if none. - * - * @param packet - * the packet that may include the MUCUser extension. - * @return the MUCUser found in the packet. - */ - public static MUCUser getMUCUserExtension(Packet packet) { - if (packet != null) - for (PacketExtension extension : packet.getExtensions()) - if (extension instanceof MUCUser) - return (MUCUser) extension; - return null; - } + /** + * Returns the MUCUser packet extension included in the packet or + * null if none. + * + * @param packet the packet that may include the MUCUser extension. + * @return the MUCUser found in the packet. + */ + public static MUCUser getMUCUserExtension(Packet packet) { + if (packet != null) + for (PacketExtension extension : packet.getExtensions()) + if (extension instanceof MUCUser) + return (MUCUser) extension; + return null; + } } diff --git a/app/src/main/java/com/xabber/xmpp/muc/Role.java b/app/src/main/java/com/xabber/xmpp/muc/Role.java index e945666879..6ba1e23791 100644 --- a/app/src/main/java/com/xabber/xmpp/muc/Role.java +++ b/app/src/main/java/com/xabber/xmpp/muc/Role.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,30 +18,29 @@ /** * Role in the room. - * + *

* http://xmpp.org/extensions/xep-0045.html#roles - * + * * @author alexander.ivanov - * */ public enum Role { - none, + none, - visitor, + visitor, - participant, + participant, - moderator; + moderator; - public static Role fromString(String value) throws NoSuchElementException { - if (value == null) - throw new NoSuchElementException(); - try { - return valueOf(value); - } catch (IllegalArgumentException e) { - throw new NoSuchElementException(); - } - } + public static Role fromString(String value) throws NoSuchElementException { + if (value == null) + throw new NoSuchElementException(); + try { + return valueOf(value); + } catch (IllegalArgumentException e) { + throw new NoSuchElementException(); + } + } } diff --git a/app/src/main/java/com/xabber/xmpp/receipt/Received.java b/app/src/main/java/com/xabber/xmpp/receipt/Received.java index 5ea3180806..960f428619 100644 --- a/app/src/main/java/com/xabber/xmpp/receipt/Received.java +++ b/app/src/main/java/com/xabber/xmpp/receipt/Received.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -23,46 +23,45 @@ /** * Receipt extension. - * + *

* http://xmpp.org/extensions/xep-0184.html - * + * * @author alexander.ivanov - * */ public class Received extends PacketExtension { - public static final String ELEMENT_NAME = "received"; - public static final String NAMESPACE = Request.NAMESPACE; - public static final String ID_ATTRIBUTE = "id"; + public static final String ELEMENT_NAME = "received"; + public static final String NAMESPACE = Request.NAMESPACE; + public static final String ID_ATTRIBUTE = "id"; - private final String id; + private final String id; - public Received(String id) { - this.id = id; - } + public Received(String id) { + this.id = id; + } - public String getId() { - return id; - } + public String getId() { + return id; + } - @Override - public String getElementName() { - return ELEMENT_NAME; - } + @Override + public String getElementName() { + return ELEMENT_NAME; + } - @Override - public String getNamespace() { - return NAMESPACE; - } + @Override + public String getNamespace() { + return NAMESPACE; + } - @Override - public void serializeContent(XmlSerializer serializer) throws IOException { - SerializerUtils.setTextAttribute(serializer, ID_ATTRIBUTE, id); - } + @Override + public void serializeContent(XmlSerializer serializer) throws IOException { + SerializerUtils.setTextAttribute(serializer, ID_ATTRIBUTE, id); + } - @Override - public boolean isValid() { - return id != null; - } + @Override + public boolean isValid() { + return id != null; + } } diff --git a/app/src/main/java/com/xabber/xmpp/receipt/ReceivedProvider.java b/app/src/main/java/com/xabber/xmpp/receipt/ReceivedProvider.java index 2cb0ce99d5..44aa0bfa3d 100644 --- a/app/src/main/java/com/xabber/xmpp/receipt/ReceivedProvider.java +++ b/app/src/main/java/com/xabber/xmpp/receipt/ReceivedProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,10 +20,10 @@ public class ReceivedProvider extends AbstractExtensionProvider { - @Override - protected Received createInstance(XmlPullParser parser) { - return new Received(parser.getAttributeValue(null, - Received.ID_ATTRIBUTE)); - } + @Override + protected Received createInstance(XmlPullParser parser) { + return new Received(parser.getAttributeValue(null, + Received.ID_ATTRIBUTE)); + } } diff --git a/app/src/main/java/com/xabber/xmpp/receipt/Request.java b/app/src/main/java/com/xabber/xmpp/receipt/Request.java index 10f44840ea..18d7b9a5bf 100644 --- a/app/src/main/java/com/xabber/xmpp/receipt/Request.java +++ b/app/src/main/java/com/xabber/xmpp/receipt/Request.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -22,37 +22,36 @@ /** * Request receipt extension. - * + *

* http://xmpp.org/extensions/xep-0184.html - * + * * @author alexander.ivanov - * */ public class Request extends PacketExtension { - public static final String ELEMENT_NAME = "request"; - public static final String NAMESPACE = "urn:xmpp:receipts"; + public static final String ELEMENT_NAME = "request"; + public static final String NAMESPACE = "urn:xmpp:receipts"; - public Request() { - } + public Request() { + } - @Override - public String getElementName() { - return ELEMENT_NAME; - } + @Override + public String getElementName() { + return ELEMENT_NAME; + } - @Override - public String getNamespace() { - return NAMESPACE; - } + @Override + public String getNamespace() { + return NAMESPACE; + } - @Override - public void serializeContent(XmlSerializer serializer) throws IOException { - } + @Override + public void serializeContent(XmlSerializer serializer) throws IOException { + } - @Override - public boolean isValid() { - return true; - } + @Override + public boolean isValid() { + return true; + } } diff --git a/app/src/main/java/com/xabber/xmpp/receipt/RequestProvider.java b/app/src/main/java/com/xabber/xmpp/receipt/RequestProvider.java index b346b8660d..6236b11edc 100644 --- a/app/src/main/java/com/xabber/xmpp/receipt/RequestProvider.java +++ b/app/src/main/java/com/xabber/xmpp/receipt/RequestProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,9 +20,9 @@ public class RequestProvider extends AbstractExtensionProvider { - @Override - protected Request createInstance(XmlPullParser parser) { - return new Request(); - } + @Override + protected Request createInstance(XmlPullParser parser) { + return new Request(); + } } diff --git a/app/src/main/java/com/xabber/xmpp/rsm/Set.java b/app/src/main/java/com/xabber/xmpp/rsm/Set.java index 56f2ac740f..7a401ec230 100644 --- a/app/src/main/java/com/xabber/xmpp/rsm/Set.java +++ b/app/src/main/java/com/xabber/xmpp/rsm/Set.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -23,157 +23,154 @@ /** * Result Set Management extension. - * + *

* http://xmpp.org/extensions/xep-0085.html - * + * * @author alexander.ivanov - * */ public class Set extends PacketExtension { - public static final String NAMESPACE = "http://jabber.org/protocol/rsm"; - public static final String ELEMENT_NAME = "set"; - - static final String AFTER_NAME = "after"; - static final String BEFORE_NAME = "before"; - static final String COUNT_NAME = "count"; - static final String FIRST_NAME = "first"; - static final String INDEX_ATTRIBUTE = "index"; - static final String INDEX_NAME = "index"; - static final String LAST_NAME = "last"; - static final String MAX_NAME = "max"; - - private String after; - private String before; - private Integer count; - private String first; - private Integer firstIndex; - private Integer index; - private String last; - private Integer max; - - @Override - public void serializeContent(XmlSerializer serializer) throws IOException { - if (after != null) - SerializerUtils.addTextTag(serializer, AFTER_NAME, after); - if (before != null) - SerializerUtils.addTextTag(serializer, BEFORE_NAME, before); - if (count != null) - SerializerUtils.addIntegerTag(serializer, COUNT_NAME, count); - if (first != null) { - serializer.startTag(null, FIRST_NAME); - if (firstIndex != null) - SerializerUtils.setIntegerAttribute(serializer, - INDEX_ATTRIBUTE, firstIndex); - serializer.text(first); - serializer.endTag(null, FIRST_NAME); - } - if (index != null) - SerializerUtils.addIntegerTag(serializer, INDEX_NAME, index); - if (last != null) - SerializerUtils.addTextTag(serializer, LAST_NAME, last); - if (max != null) - SerializerUtils.addIntegerTag(serializer, MAX_NAME, max); - } - - @Override - public boolean isValid() { - return true; - } - - @Override - public String getElementName() { - return ELEMENT_NAME; - } - - @Override - public String getNamespace() { - return NAMESPACE; - } - - public String getAfter() { - return after; - } - - public void setAfter(String after) { - this.after = after; - } - - public String getBefore() { - return before; - } - - public void setBefore(String before) { - this.before = before; - } - - public Integer getCount() { - return count; - } - - public void setCount(Integer count) { - this.count = count; - } - - public String getFirst() { - return first; - } - - public void setFirst(String first) { - this.first = first; - } - - public Integer getFirstIndex() { - return firstIndex; - } - - public void setFirstIndex(Integer firstIndex) { - this.firstIndex = firstIndex; - } - - public Integer getIndex() { - return index; - } - - public void setIndex(Integer index) { - this.index = index; - } - - public String getLast() { - return last; - } - - public void setLast(String last) { - this.last = last; - } - - public Integer getMax() { - return max; - } - - public void setMax(Integer max) { - this.max = max; - } - - /** - * @param received - * number of currently received elements. - * @return true if forward pagination willn't receive any more - * elements. - */ - public boolean isForwardFinished(int received) { - return last == null - || (firstIndex != null && count != null && count - firstIndex == received); - } - - /** - * @param received - * number of currently received elements. - * @return true if backward pagination willn't receive any more - * elements. - */ - public boolean isBackwardFinished(int received) { - return first == null || (firstIndex != null && firstIndex == 0); - } + public static final String NAMESPACE = "http://jabber.org/protocol/rsm"; + public static final String ELEMENT_NAME = "set"; + + static final String AFTER_NAME = "after"; + static final String BEFORE_NAME = "before"; + static final String COUNT_NAME = "count"; + static final String FIRST_NAME = "first"; + static final String INDEX_ATTRIBUTE = "index"; + static final String INDEX_NAME = "index"; + static final String LAST_NAME = "last"; + static final String MAX_NAME = "max"; + + private String after; + private String before; + private Integer count; + private String first; + private Integer firstIndex; + private Integer index; + private String last; + private Integer max; + + @Override + public void serializeContent(XmlSerializer serializer) throws IOException { + if (after != null) + SerializerUtils.addTextTag(serializer, AFTER_NAME, after); + if (before != null) + SerializerUtils.addTextTag(serializer, BEFORE_NAME, before); + if (count != null) + SerializerUtils.addIntegerTag(serializer, COUNT_NAME, count); + if (first != null) { + serializer.startTag(null, FIRST_NAME); + if (firstIndex != null) + SerializerUtils.setIntegerAttribute(serializer, + INDEX_ATTRIBUTE, firstIndex); + serializer.text(first); + serializer.endTag(null, FIRST_NAME); + } + if (index != null) + SerializerUtils.addIntegerTag(serializer, INDEX_NAME, index); + if (last != null) + SerializerUtils.addTextTag(serializer, LAST_NAME, last); + if (max != null) + SerializerUtils.addIntegerTag(serializer, MAX_NAME, max); + } + + @Override + public boolean isValid() { + return true; + } + + @Override + public String getElementName() { + return ELEMENT_NAME; + } + + @Override + public String getNamespace() { + return NAMESPACE; + } + + public String getAfter() { + return after; + } + + public void setAfter(String after) { + this.after = after; + } + + public String getBefore() { + return before; + } + + public void setBefore(String before) { + this.before = before; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + public String getFirst() { + return first; + } + + public void setFirst(String first) { + this.first = first; + } + + public Integer getFirstIndex() { + return firstIndex; + } + + public void setFirstIndex(Integer firstIndex) { + this.firstIndex = firstIndex; + } + + public Integer getIndex() { + return index; + } + + public void setIndex(Integer index) { + this.index = index; + } + + public String getLast() { + return last; + } + + public void setLast(String last) { + this.last = last; + } + + public Integer getMax() { + return max; + } + + public void setMax(Integer max) { + this.max = max; + } + + /** + * @param received number of currently received elements. + * @return true if forward pagination willn't receive any more + * elements. + */ + public boolean isForwardFinished(int received) { + return last == null + || (firstIndex != null && count != null && count - firstIndex == received); + } + + /** + * @param received number of currently received elements. + * @return true if backward pagination willn't receive any more + * elements. + */ + public boolean isBackwardFinished(int received) { + return first == null || (firstIndex != null && firstIndex == 0); + } } diff --git a/app/src/main/java/com/xabber/xmpp/rsm/SetProvider.java b/app/src/main/java/com/xabber/xmpp/rsm/SetProvider.java index abc0a4e8a1..f3292a0543 100644 --- a/app/src/main/java/com/xabber/xmpp/rsm/SetProvider.java +++ b/app/src/main/java/com/xabber/xmpp/rsm/SetProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -21,37 +21,37 @@ public class SetProvider extends AbstractExtensionProvider { - @Override - protected Set createInstance(XmlPullParser parser) { - return new Set(); - } + @Override + protected Set createInstance(XmlPullParser parser) { + return new Set(); + } - @Override - protected boolean parseInner(XmlPullParser parser, Set instance) - throws Exception { - if (super.parseInner(parser, instance)) - return true; - if (Set.AFTER_NAME.equals(parser.getName())) - instance.setAfter(ProviderUtils.parseText(parser)); - else if (Set.AFTER_NAME.equals(parser.getName())) - instance.setAfter(ProviderUtils.parseText(parser)); - else if (Set.BEFORE_NAME.equals(parser.getName())) - instance.setBefore(ProviderUtils.parseText(parser)); - else if (Set.COUNT_NAME.equals(parser.getName())) - instance.setCount(ProviderUtils.parseInteger(parser)); - else if (Set.FIRST_NAME.equals(parser.getName())) { - instance.setFirstIndex(ProviderUtils.parseInteger(parser - .getAttributeValue(null, Set.INDEX_ATTRIBUTE))); - instance.setFirst(ProviderUtils.parseText(parser)); - } else if (Set.INDEX_NAME.equals(parser.getName())) - instance.setIndex(ProviderUtils.parseInteger(parser)); - else if (Set.LAST_NAME.equals(parser.getName())) - instance.setLast(ProviderUtils.parseText(parser)); - else if (Set.MAX_NAME.equals(parser.getName())) - instance.setMax(ProviderUtils.parseInteger(parser)); - else - return false; - return true; - } + @Override + protected boolean parseInner(XmlPullParser parser, Set instance) + throws Exception { + if (super.parseInner(parser, instance)) + return true; + if (Set.AFTER_NAME.equals(parser.getName())) + instance.setAfter(ProviderUtils.parseText(parser)); + else if (Set.AFTER_NAME.equals(parser.getName())) + instance.setAfter(ProviderUtils.parseText(parser)); + else if (Set.BEFORE_NAME.equals(parser.getName())) + instance.setBefore(ProviderUtils.parseText(parser)); + else if (Set.COUNT_NAME.equals(parser.getName())) + instance.setCount(ProviderUtils.parseInteger(parser)); + else if (Set.FIRST_NAME.equals(parser.getName())) { + instance.setFirstIndex(ProviderUtils.parseInteger(parser + .getAttributeValue(null, Set.INDEX_ATTRIBUTE))); + instance.setFirst(ProviderUtils.parseText(parser)); + } else if (Set.INDEX_NAME.equals(parser.getName())) + instance.setIndex(ProviderUtils.parseInteger(parser)); + else if (Set.LAST_NAME.equals(parser.getName())) + instance.setLast(ProviderUtils.parseText(parser)); + else if (Set.MAX_NAME.equals(parser.getName())) + instance.setMax(ProviderUtils.parseInteger(parser)); + else + return false; + return true; + } } diff --git a/app/src/main/java/com/xabber/xmpp/ssn/DisclosureValue.java b/app/src/main/java/com/xabber/xmpp/ssn/DisclosureValue.java index f94726608d..b01682f68c 100644 --- a/app/src/main/java/com/xabber/xmpp/ssn/DisclosureValue.java +++ b/app/src/main/java/com/xabber/xmpp/ssn/DisclosureValue.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,41 +20,40 @@ /** * Disclosure parameter values. - * + *

* http://xmpp.org/extensions/xep-0155.html#parameters - * + * * @author alexander.ivanov - * */ public enum DisclosureValue { - never( - "Entities guarantee no disclosure features exist (not even disabled features)"), + never( + "Entities guarantee no disclosure features exist (not even disabled features)"), - disabled( - "Entities MUST NOT disclose (except for those disclosures that are required by law)"), + disabled( + "Entities MUST NOT disclose (except for those disclosures that are required by law)"), - enabled("Entities MAY disclose"); + enabled("Entities MAY disclose"); - private final String label; + private final String label; - private DisclosureValue(String label) { - this.label = label; - } + DisclosureValue(String label) { + this.label = label; + } - public Option createOption() { - return new Option(label, name()); - } + public Option createOption() { + return new Option(label, name()); + } - public static DisclosureValue fromString(String value) - throws NoSuchElementException { - if (value == null) - throw new NoSuchElementException(); - try { - return valueOf(value); - } catch (IllegalArgumentException e) { - throw new NoSuchElementException(); - } - } + public static DisclosureValue fromString(String value) + throws NoSuchElementException { + if (value == null) + throw new NoSuchElementException(); + try { + return valueOf(value); + } catch (IllegalArgumentException e) { + throw new NoSuchElementException(); + } + } } diff --git a/app/src/main/java/com/xabber/xmpp/ssn/Feature.java b/app/src/main/java/com/xabber/xmpp/ssn/Feature.java index 64e331526e..dec4d7dcb3 100644 --- a/app/src/main/java/com/xabber/xmpp/ssn/Feature.java +++ b/app/src/main/java/com/xabber/xmpp/ssn/Feature.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -31,248 +31,247 @@ /** * Packet extension for Stanza Session Negotiation. - * + *

* http://xmpp.org/extensions/xep-0155.html - * + * * @author alexander.ivanov - * */ public class Feature extends PacketExtension { - private static final String NAMESPACE = "http://jabber.org/protocol/feature-neg"; - private static final String ELEMENT_NAME = "feature"; - - public static final String FORM_TYPE_FIELD = "FORM_TYPE"; - public static final String FORM_TYPE_VALUE = "urn:xmpp:ssn"; - private static final String ACCEPT_FIELD = "accept"; - private static final String RENEGOTIATE_FIELD = "renegotiate"; - private static final String TERMINATE_FIELD = "terminate"; - public static final String LOGGING_FIELD = "logging"; - private static final String DISCLOSURE_FIELD = "disclosure"; - private static final String SECURITY_FIELD = "security"; - - private DataForm dataForm; - - public Feature() { - } - - public Feature(DataForm dataForm) { - this(); - setDataForm(dataForm); - } - - @Override - public void serializeContent(XmlSerializer serializer) throws IOException { - dataForm.serialize(serializer); - } - - @Override - public boolean isValid() { - if (dataForm == null) - return false; - DataFormType dataFormType = getDataFormType(); - if (dataFormType == null || dataFormType == DataFormType.cancel) - return false; - int selected = 0; - if (getAcceptValue() != null) - selected += 1; - if (getRenegotiateValue() != null) - selected += 1; - if (getTerminateValue() != null) { - if (!getTerminateValue() - || getDataFormType() != DataFormType.submit) - return false; - selected += 1; - } - if (selected != 1) - return false; - return FORM_TYPE_VALUE.equals(getValue(FORM_TYPE_FIELD)); - } - - @Override - public String getElementName() { - return ELEMENT_NAME; - } - - @Override - public String getNamespace() { - return NAMESPACE; - } - - public DataForm getDataForm() { - return dataForm; - } - - public void setDataForm(DataForm dataForm) { - this.dataForm = dataForm; - } - - static public DataForm createDataForm(DataFormType type) { - DataForm dataForm = new DataForm(type.toString()); - FormField typeField = new FormField(FORM_TYPE_FIELD); - typeField.addValue(FORM_TYPE_VALUE); - typeField.setType(FormField.TYPE_HIDDEN); - dataForm.addField(typeField); - return dataForm; - } - - static private void addRequiredBooleanField(DataForm dataForm, String name, - String label, boolean value) { - FormField field = new FormField(name); - field.setRequired(true); - field.setLabel(label); - field.addValue(Boolean.valueOf(value).toString()); - field.setType(FormField.TYPE_BOOLEAN); - dataForm.addField(field); - } - - static public void addAcceptField(DataForm dataForm, boolean value) { - addRequiredBooleanField(dataForm, ACCEPT_FIELD, "Accept this session?", - value); - } - - static public void addRenegotiateField(DataForm dataForm, boolean value) { - addRequiredBooleanField(dataForm, RENEGOTIATE_FIELD, "Renegotiate?", - value); - } - - static public void addTerminateField(DataForm dataForm) { - addRequiredBooleanField(dataForm, TERMINATE_FIELD, null, true); - } - - static public void addLoggingField(DataForm dataForm, - LoggingValue[] options, LoggingValue value) { - FormField field = new FormField(LOGGING_FIELD); - field.setRequired(true); - field.setLabel("Message logging"); - field.setType(FormField.TYPE_LIST_SINGLE); - if (options != null) - for (LoggingValue loggingValue : options) - field.addOption(loggingValue.createOption()); - field.addValue(value.name()); - dataForm.addField(field); - } - - public static void addDisclosureField(DataForm dataForm, - DisclosureValue[] options, DisclosureValue value) { - FormField field = new FormField(DISCLOSURE_FIELD); - field.setRequired(false); - field.setLabel("Disclosure of content, decryption keys or identities"); - field.setType(FormField.TYPE_LIST_SINGLE); - if (options != null) - for (DisclosureValue loggingValue : options) - field.addOption(loggingValue.createOption()); - field.addValue(value.name()); - dataForm.addField(field); - } - - public static void addSecurityField(DataForm dataForm, - SecurityValue[] options, SecurityValue value) { - FormField field = new FormField(SECURITY_FIELD); - field.setRequired(false); - field.setLabel("Minimum security level"); - field.setType(FormField.TYPE_LIST_SINGLE); - if (options != null) - for (SecurityValue loggingValue : options) - field.addOption(loggingValue.createOption()); - field.addValue(value.name()); - dataForm.addField(field); - } - - private FormField getField(String name) { - for (Iterator i = dataForm.getFields(); i.hasNext();) { - FormField formField = i.next(); - if (name.equals(formField.getVariable())) - return formField; - } - return null; - } - - public Collection getLoggingOptions() { - FormField field = getField(LOGGING_FIELD); - if (field == null) - return null; - Collection collection = new ArrayList(); - Iterator

* http://xmpp.org/extensions/xep-0155.html#parameters - * + * * @author alexander.ivanov - * */ public enum LoggingValue { - may("Allow message logging"), + may("Allow message logging"), - mustnot("Disallow all message logging"); + mustnot("Disallow all message logging"); - private final String label; + private final String label; - private LoggingValue(String label) { - this.label = label; - } + LoggingValue(String label) { + this.label = label; + } - public Option createOption() { - return new Option(label, name()); - } + public Option createOption() { + return new Option(label, name()); + } - public static LoggingValue fromString(String value) - throws NoSuchElementException { - if (value == null) - throw new NoSuchElementException(); - try { - return valueOf(value); - } catch (IllegalArgumentException e) { - throw new NoSuchElementException(); - } - } + public static LoggingValue fromString(String value) + throws NoSuchElementException { + if (value == null) + throw new NoSuchElementException(); + try { + return valueOf(value); + } catch (IllegalArgumentException e) { + throw new NoSuchElementException(); + } + } } diff --git a/app/src/main/java/com/xabber/xmpp/ssn/SecurityValue.java b/app/src/main/java/com/xabber/xmpp/ssn/SecurityValue.java index b9a62e01b6..7510b4a92e 100644 --- a/app/src/main/java/com/xabber/xmpp/ssn/SecurityValue.java +++ b/app/src/main/java/com/xabber/xmpp/ssn/SecurityValue.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,39 +20,38 @@ /** * Security parameter values. - * + *

* http://xmpp.org/extensions/xep-0155.html#parameters - * + * * @author alexander.ivanov - * */ public enum SecurityValue { - none("Secure connections not required"), + none("Secure connections not required"), - c2s("Both parties must be securely connected to their servers"), + c2s("Both parties must be securely connected to their servers"), - e2e("Both parties must be securely connected to each other"); + e2e("Both parties must be securely connected to each other"); - private final String label; + private final String label; - private SecurityValue(String label) { - this.label = label; - } + SecurityValue(String label) { + this.label = label; + } - public Option createOption() { - return new Option(label, name()); - } + public Option createOption() { + return new Option(label, name()); + } - public static SecurityValue fromString(String value) - throws NoSuchElementException { - if (value == null) - throw new NoSuchElementException(); - try { - return valueOf(value); - } catch (IllegalArgumentException e) { - throw new NoSuchElementException(); - } - } + public static SecurityValue fromString(String value) + throws NoSuchElementException { + if (value == null) + throw new NoSuchElementException(); + try { + return valueOf(value); + } catch (IllegalArgumentException e) { + throw new NoSuchElementException(); + } + } } diff --git a/app/src/main/java/com/xabber/xmpp/time/Time.java b/app/src/main/java/com/xabber/xmpp/time/Time.java index 1839d41c96..f0afd7c8fe 100644 --- a/app/src/main/java/com/xabber/xmpp/time/Time.java +++ b/app/src/main/java/com/xabber/xmpp/time/Time.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -24,92 +24,90 @@ /** * Entity time packet. - * + *

* http://xmpp.org/extensions/xep-0202.html - * + * * @author alexander.ivanov - * */ public class Time extends IQ { - public static final String ELEMENT_NAME = "time"; - public static final String NAMESPACE = "urn:xmpp:time"; - - public static final String TZO_NAME = "tzo"; - public static final String UTC_NAME = "utc"; - - /** - * Time zone offset in minutes. - */ - private Integer tzo; - private Date utc; - private final Date created; - - public Time() { - created = new Date(); - } - - @Override - public void serializeContent(XmlSerializer serializer) throws IOException { - if (tzo != null) { - String value; - if (tzo == 0) { - value = "z"; - } else { - boolean positive = tzo > 0; - int abs = Math.abs(tzo); - value = String.format("%s%02d:%02d", positive ? "+" : "-", - (abs / 60) % 24, abs % 60); - } - SerializerUtils.addTextTag(serializer, TZO_NAME, value); - } - if (utc != null) - SerializerUtils.addDateTimeTag(serializer, UTC_NAME, utc); - } - - @Override - public boolean isValid() { - return tzo != null && utc != null; - } - - @Override - public String getElementName() { - return ELEMENT_NAME; - } - - @Override - public String getNamespace() { - return NAMESPACE; - } - - /** - * @return Time zone offset in minutes. - */ - public Integer getTzo() { - return tzo; - } - - /** - * @param tzo - * Time zone offset in minutes. - */ - public void setTzo(Integer tzo) { - this.tzo = tzo; - } - - public Date getUtc() { - return utc; - } - - public void setUtc(Date utc) { - this.utc = utc; - } - - /** - * @return Time when object has been created. - */ - public Date getCreated() { - return created; - } + public static final String ELEMENT_NAME = "time"; + public static final String NAMESPACE = "urn:xmpp:time"; + + public static final String TZO_NAME = "tzo"; + public static final String UTC_NAME = "utc"; + + /** + * Time zone offset in minutes. + */ + private Integer tzo; + private Date utc; + private final Date created; + + public Time() { + created = new Date(); + } + + @Override + public void serializeContent(XmlSerializer serializer) throws IOException { + if (tzo != null) { + String value; + if (tzo == 0) { + value = "z"; + } else { + boolean positive = tzo > 0; + int abs = Math.abs(tzo); + value = String.format("%s%02d:%02d", positive ? "+" : "-", + (abs / 60) % 24, abs % 60); + } + SerializerUtils.addTextTag(serializer, TZO_NAME, value); + } + if (utc != null) + SerializerUtils.addDateTimeTag(serializer, UTC_NAME, utc); + } + + @Override + public boolean isValid() { + return tzo != null && utc != null; + } + + @Override + public String getElementName() { + return ELEMENT_NAME; + } + + @Override + public String getNamespace() { + return NAMESPACE; + } + + /** + * @return Time zone offset in minutes. + */ + public Integer getTzo() { + return tzo; + } + + /** + * @param tzo Time zone offset in minutes. + */ + public void setTzo(Integer tzo) { + this.tzo = tzo; + } + + public Date getUtc() { + return utc; + } + + public void setUtc(Date utc) { + this.utc = utc; + } + + /** + * @return Time when object has been created. + */ + public Date getCreated() { + return created; + } } diff --git a/app/src/main/java/com/xabber/xmpp/time/TimeProvider.java b/app/src/main/java/com/xabber/xmpp/time/TimeProvider.java index 7f81b63d5e..3b7b628bd1 100644 --- a/app/src/main/java/com/xabber/xmpp/time/TimeProvider.java +++ b/app/src/main/java/com/xabber/xmpp/time/TimeProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -24,44 +24,44 @@ public class TimeProvider extends AbstractIQProvider

* http://xmpp.org/extensions/xep-0147.html - * + * * @author alexander.ivanov - * */ public class XMPPUri { - private static final String XMPP_SCHEME = "xmpp"; + private static final String XMPP_SCHEME = "xmpp"; - private static final Pattern XMPP_PATTERN = Pattern - .compile("xmpp\\:(?:(?:[" - + Patterns.GOOD_IRI_CHAR - + "\\;\\/\\?\\@\\&\\=\\#\\~\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])" - + "|(?:\\%[a-fA-F0-9]{2}))+"); + private static final Pattern XMPP_PATTERN = Pattern + .compile("xmpp\\:(?:(?:[" + + Patterns.GOOD_IRI_CHAR + + "\\;\\/\\?\\@\\&\\=\\#\\~\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])" + + "|(?:\\%[a-fA-F0-9]{2}))+"); - private final String authority; - private final String path; - private final String queryType; - private final HashMap> values; + private final String authority; + private final String path; + private final String queryType; + private final HashMap> values; - public static XMPPUri parse(Uri uri) throws IllegalArgumentException { - return new XMPPUri(uri); - } + public static XMPPUri parse(Uri uri) throws IllegalArgumentException { + return new XMPPUri(uri); + } - private XMPPUri(Uri uri) throws IllegalArgumentException { - if (uri == null) - throw new IllegalArgumentException(); - if (!XMPP_SCHEME.equals(uri.getScheme())) - throw new IllegalArgumentException(); - // Fix processing path without leading slash - uri = Uri.parse(uri.getEncodedSchemeSpecificPart()); - authority = uri.getAuthority(); - if (uri.getPath() == null) - throw new IllegalArgumentException(); - if (uri.getPath().startsWith("/")) - path = uri.getPath().substring(1); - else - path = uri.getPath(); - values = new HashMap>(); - String query = uri.getEncodedQuery(); - String action = null; - if (query != null) { - String parts[] = query.split(";"); - for (String part : parts) - if (action == null) { - if (part.contains("=")) - throw new IllegalArgumentException(); - action = part; - } else { - int index = part.indexOf("="); - if (index == -1) - continue; - String key = part.substring(0, index); - String value = part.substring(index + 1); - ArrayList list = values.get(key); - if (list == null) { - list = new ArrayList(); - values.put(key, list); - } - list.add(Uri.decode(value)); - } - } - queryType = action; - } + private XMPPUri(Uri uri) throws IllegalArgumentException { + if (uri == null) + throw new IllegalArgumentException(); + if (!XMPP_SCHEME.equals(uri.getScheme())) + throw new IllegalArgumentException(); + // Fix processing path without leading slash + uri = Uri.parse(uri.getEncodedSchemeSpecificPart()); + authority = uri.getAuthority(); + if (uri.getPath() == null) + throw new IllegalArgumentException(); + if (uri.getPath().startsWith("/")) + path = uri.getPath().substring(1); + else + path = uri.getPath(); + values = new HashMap>(); + String query = uri.getEncodedQuery(); + String action = null; + if (query != null) { + String parts[] = query.split(";"); + for (String part : parts) + if (action == null) { + if (part.contains("=")) + throw new IllegalArgumentException(); + action = part; + } else { + int index = part.indexOf("="); + if (index == -1) + continue; + String key = part.substring(0, index); + String value = part.substring(index + 1); + ArrayList list = values.get(key); + if (list == null) { + list = new ArrayList(); + values.put(key, list); + } + list.add(Uri.decode(value)); + } + } + queryType = action; + } - public String getAuthority() { - return authority; - } + public String getAuthority() { + return authority; + } - public String getPath() { - return path; - } + public String getPath() { + return path; + } - public String getQueryType() { - return queryType; - } + public String getQueryType() { + return queryType; + } - public ArrayList getValues(String queryKey) { - return values.get(queryKey); - } + public ArrayList getValues(String queryKey) { + return values.get(queryKey); + } - @Override - public String toString() { - return path + " : " + queryType + " : " + values; - } + @Override + public String toString() { + return path + " : " + queryType + " : " + values; + } - /** - * Update spannable with XMPP URI links. - * - * @param spannable - * @return Where spannable was modified. - */ - public static boolean addLinks(Spannable spannable) { - return Linkify.addLinks(spannable, XMPP_PATTERN, "xmpp"); - } + /** + * Update spannable with XMPP URI links. + * + * @param spannable + * @return Where spannable was modified. + */ + public static boolean addLinks(Spannable spannable) { + return Linkify.addLinks(spannable, XMPP_PATTERN, "xmpp"); + } } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/AbstractBinaryData.java b/app/src/main/java/com/xabber/xmpp/vcard/AbstractBinaryData.java index 56a246618f..020940d42a 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/AbstractBinaryData.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/AbstractBinaryData.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -23,43 +23,43 @@ public abstract class AbstractBinaryData extends AbstractData { - /** - * Limit received encoded data size. - */ - public static final int MAX_ENCODED_DATA_SIZE = 256 * 1024; + /** + * Limit received encoded data size. + */ + public static final int MAX_ENCODED_DATA_SIZE = 256 * 1024; - public static final String TYPE_NAME = "TYPE"; - public static final String BINVAL_NAME = "BINVAL"; + public static final String TYPE_NAME = "TYPE"; + public static final String BINVAL_NAME = "BINVAL"; - private String type; - private byte[] data; + private String type; + private byte[] data; - public String getType() { - return type; - } + public String getType() { + return type; + } - public void setType(String type) { - this.type = type; - } + public void setType(String type) { + this.type = type; + } - public byte[] getData() { - return data; - } + public byte[] getData() { + return data; + } - public void setData(byte[] data) { - this.data = data; - } + public void setData(byte[] data) { + this.data = data; + } - @Override - public boolean isValid() { - return type != null && data != null; - } + @Override + public boolean isValid() { + return type != null && data != null; + } - @Override - protected void writeBody(XmlSerializer serializer) throws IOException { - SerializerUtils.addTextTag(serializer, TYPE_NAME, type); - SerializerUtils.addTextTag(serializer, BINVAL_NAME, - StringUtils.encodeBase64(data)); - } + @Override + protected void writeBody(XmlSerializer serializer) throws IOException { + SerializerUtils.addTextTag(serializer, TYPE_NAME, type); + SerializerUtils.addTextTag(serializer, BINVAL_NAME, + StringUtils.encodeBase64(data)); + } } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/AbstractData.java b/app/src/main/java/com/xabber/xmpp/vcard/AbstractData.java index 4cfde992e2..eee73c06ad 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/AbstractData.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/AbstractData.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -22,16 +22,16 @@ public abstract class AbstractData implements Instance { - @Override - public void serialize(XmlSerializer serializer) throws IOException { - serializer.startTag(null, getElementName()); - writeBody(serializer); - serializer.endTag(null, getElementName()); - } + @Override + public void serialize(XmlSerializer serializer) throws IOException { + serializer.startTag(null, getElementName()); + writeBody(serializer); + serializer.endTag(null, getElementName()); + } - public abstract String getElementName(); + public abstract String getElementName(); - protected abstract void writeBody(XmlSerializer serializer) - throws IOException; + protected abstract void writeBody(XmlSerializer serializer) + throws IOException; } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/AbstractDataProvider.java b/app/src/main/java/com/xabber/xmpp/vcard/AbstractDataProvider.java index 12240bab2d..b1e639bb88 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/AbstractDataProvider.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/AbstractDataProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -23,72 +23,72 @@ import com.xabber.xmpp.ProviderUtils; abstract class AbstractDataProvider> - extends AbstractProvider { + extends AbstractProvider { - @Override - protected boolean parseInner(XmlPullParser parser, Inner instance) - throws Exception { - if (super.parseInner(parser, instance)) - return true; - if (instance.getPayload() == null) - if (!createPayload(parser, instance)) - return false; - return inflatePayload(parser, instance); - } + @Override + protected boolean parseInner(XmlPullParser parser, Inner instance) + throws Exception { + if (super.parseInner(parser, instance)) + return true; + if (instance.getPayload() == null) + if (!createPayload(parser, instance)) + return false; + return inflatePayload(parser, instance); + } - protected boolean createPayload(XmlPullParser parser, Inner instance) - throws Exception { - if (AbstractBinaryData.TYPE_NAME.equals(parser.getName()) - || AbstractBinaryData.BINVAL_NAME.equals(parser.getName())) - instance.setPayload(createBinaryData()); - else if (AbstractExternalData.EXTVAL_NAME.equals(parser.getName())) - instance.setPayload(createExternalData()); - else - return false; - return true; - } + protected boolean createPayload(XmlPullParser parser, Inner instance) + throws Exception { + if (AbstractBinaryData.TYPE_NAME.equals(parser.getName()) + || AbstractBinaryData.BINVAL_NAME.equals(parser.getName())) + instance.setPayload(createBinaryData()); + else if (AbstractExternalData.EXTVAL_NAME.equals(parser.getName())) + instance.setPayload(createExternalData()); + else + return false; + return true; + } - protected abstract T createBinaryData(); + protected abstract T createBinaryData(); - protected abstract T createExternalData(); + protected abstract T createExternalData(); - protected boolean inflatePayload(XmlPullParser parser, Inner instance) - throws Exception { - if (instance.getPayload() instanceof AbstractBinaryData) - return inflateBinaryData(parser, - (AbstractBinaryData) instance.getPayload()); - else if (instance.getPayload() instanceof AbstractExternalData) - return inflateExternalData(parser, - (AbstractExternalData) instance.getPayload()); - else - return false; - } + protected boolean inflatePayload(XmlPullParser parser, Inner instance) + throws Exception { + if (instance.getPayload() instanceof AbstractBinaryData) + return inflateBinaryData(parser, + (AbstractBinaryData) instance.getPayload()); + else if (instance.getPayload() instanceof AbstractExternalData) + return inflateExternalData(parser, + (AbstractExternalData) instance.getPayload()); + else + return false; + } - protected boolean inflateBinaryData(XmlPullParser parser, - AbstractBinaryData payload) throws Exception { - if (AbstractBinaryData.TYPE_NAME.equals(parser.getName())) - payload.setType(ProviderUtils.parseText(parser)); - else if (AbstractBinaryData.BINVAL_NAME.equals(parser.getName())) { - String value; - try { - value = ProviderUtils.parseText(parser, - AbstractBinaryData.MAX_ENCODED_DATA_SIZE); - } catch (OverflowReceiverBufferException e) { - return true; - } - payload.setData(StringUtils.decodeBase64(value)); - } else - return false; - return true; - } + protected boolean inflateBinaryData(XmlPullParser parser, + AbstractBinaryData payload) throws Exception { + if (AbstractBinaryData.TYPE_NAME.equals(parser.getName())) + payload.setType(ProviderUtils.parseText(parser)); + else if (AbstractBinaryData.BINVAL_NAME.equals(parser.getName())) { + String value; + try { + value = ProviderUtils.parseText(parser, + AbstractBinaryData.MAX_ENCODED_DATA_SIZE); + } catch (OverflowReceiverBufferException e) { + return true; + } + payload.setData(StringUtils.decodeBase64(value)); + } else + return false; + return true; + } - protected boolean inflateExternalData(XmlPullParser parser, - AbstractExternalData payload) throws Exception { - if (AbstractExternalData.EXTVAL_NAME.equals(parser.getName())) - payload.setValue(ProviderUtils.parseText(parser)); - else - return false; - return true; - } + protected boolean inflateExternalData(XmlPullParser parser, + AbstractExternalData payload) throws Exception { + if (AbstractExternalData.EXTVAL_NAME.equals(parser.getName())) + payload.setValue(ProviderUtils.parseText(parser)); + else + return false; + return true; + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/xmpp/vcard/AbstractExternalData.java b/app/src/main/java/com/xabber/xmpp/vcard/AbstractExternalData.java index 34c0bb2002..c9048ac417 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/AbstractExternalData.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/AbstractExternalData.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -22,26 +22,26 @@ public abstract class AbstractExternalData extends AbstractData { - public static final String EXTVAL_NAME = "EXTVAL"; + public static final String EXTVAL_NAME = "EXTVAL"; - private String value; + private String value; - public String getValue() { - return value; - } + public String getValue() { + return value; + } - public void setValue(String value) { - this.value = value; - } + public void setValue(String value) { + this.value = value; + } - @Override - public boolean isValid() { - return value != null; - } + @Override + public boolean isValid() { + return value != null; + } - @Override - protected void writeBody(XmlSerializer serializer) throws IOException { - SerializerUtils.addTextTag(serializer, EXTVAL_NAME, value); - } + @Override + protected void writeBody(XmlSerializer serializer) throws IOException { + SerializerUtils.addTextTag(serializer, EXTVAL_NAME, value); + } } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/AbstractTypedData.java b/app/src/main/java/com/xabber/xmpp/vcard/AbstractTypedData.java index 36cb397959..30decaa6f8 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/AbstractTypedData.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/AbstractTypedData.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -23,32 +23,32 @@ import com.xabber.xmpp.Instance; public abstract class AbstractTypedData> implements - Instance { + Instance { - protected final Set types; + protected final Set types; - public AbstractTypedData() { - types = new HashSet(); - } + public AbstractTypedData() { + types = new HashSet(); + } - @Override - public void serialize(XmlSerializer serializer) throws IOException { - serializer.startTag(null, getElementName()); - for (Type type : types) { - serializer.startTag(null, type.toString()); - serializer.endTag(null, type.toString()); - } - writeBody(serializer); - serializer.endTag(null, getElementName()); - } + @Override + public void serialize(XmlSerializer serializer) throws IOException { + serializer.startTag(null, getElementName()); + for (Type type : types) { + serializer.startTag(null, type.toString()); + serializer.endTag(null, type.toString()); + } + writeBody(serializer); + serializer.endTag(null, getElementName()); + } - protected abstract String getElementName(); + protected abstract String getElementName(); - protected abstract void writeBody(XmlSerializer serializer) - throws IOException; + protected abstract void writeBody(XmlSerializer serializer) + throws IOException; - public Set getTypes() { - return types; - } + public Set getTypes() { + return types; + } } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/AbstractTypedDataProvider.java b/app/src/main/java/com/xabber/xmpp/vcard/AbstractTypedDataProvider.java index 676b7b8d88..fe07920900 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/AbstractTypedDataProvider.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/AbstractTypedDataProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,23 +20,23 @@ import com.xabber.xmpp.ProviderUtils; abstract class AbstractTypedDataProvider, Instance extends AbstractTypedData> - extends AbstractProvider { + extends AbstractProvider { - @Override - protected boolean parseInner(XmlPullParser parser, Instance instance) - throws Exception { - if (super.parseInner(parser, instance)) - return true; - String name = parser.getName(); - for (Type type : getTypes()) - if (type.toString().equals(name)) { - instance.getTypes().add(type); - ProviderUtils.skipTag(parser); - return true; - } - return false; - } + @Override + protected boolean parseInner(XmlPullParser parser, Instance instance) + throws Exception { + if (super.parseInner(parser, instance)) + return true; + String name = parser.getName(); + for (Type type : getTypes()) + if (type.toString().equals(name)) { + instance.getTypes().add(type); + ProviderUtils.skipTag(parser); + return true; + } + return false; + } - protected abstract Type[] getTypes(); + protected abstract Type[] getTypes(); } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/AbstractTypedDataWithValue.java b/app/src/main/java/com/xabber/xmpp/vcard/AbstractTypedDataWithValue.java index 5abafd1ff6..b89d7ecfb2 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/AbstractTypedDataWithValue.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/AbstractTypedDataWithValue.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -21,32 +21,32 @@ import com.xabber.xmpp.SerializerUtils; public abstract class AbstractTypedDataWithValue> extends - AbstractTypedData { + AbstractTypedData { - private String value; + private String value; - public AbstractTypedDataWithValue() { - super(); - } + public AbstractTypedDataWithValue() { + super(); + } - public String getValue() { - return value; - } + public String getValue() { + return value; + } - public void setValue(String value) { - this.value = value; - } + public void setValue(String value) { + this.value = value; + } - protected abstract String getValueName(); + protected abstract String getValueName(); - @Override - protected void writeBody(XmlSerializer serializer) throws IOException { - SerializerUtils.addTextTag(serializer, getValueName(), value); - } + @Override + protected void writeBody(XmlSerializer serializer) throws IOException { + SerializerUtils.addTextTag(serializer, getValueName(), value); + } - @Override - public boolean isValid() { - return value != null; - } + @Override + public boolean isValid() { + return value != null; + } } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/AbstractTypedDataWithValueProvider.java b/app/src/main/java/com/xabber/xmpp/vcard/AbstractTypedDataWithValueProvider.java index fb2a1294b1..21169a40d3 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/AbstractTypedDataWithValueProvider.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/AbstractTypedDataWithValueProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -19,20 +19,20 @@ import com.xabber.xmpp.ProviderUtils; public abstract class AbstractTypedDataWithValueProvider, Instance extends AbstractTypedDataWithValue> - extends AbstractTypedDataProvider { + extends AbstractTypedDataProvider { - @Override - protected boolean parseInner(XmlPullParser parser, Instance instance) - throws Exception { - if (super.parseInner(parser, instance)) - return true; - if (getValueName().equals(parser.getName())) { - instance.setValue(ProviderUtils.parseText(parser)); - return true; - } - return false; - } + @Override + protected boolean parseInner(XmlPullParser parser, Instance instance) + throws Exception { + if (super.parseInner(parser, instance)) + return true; + if (getValueName().equals(parser.getName())) { + instance.setValue(ProviderUtils.parseText(parser)); + return true; + } + return false; + } - protected abstract String getValueName(); + protected abstract String getValueName(); } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/Address.java b/app/src/main/java/com/xabber/xmpp/vcard/Address.java index b4efa6ae5e..9b54f317a8 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/Address.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/Address.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -25,34 +25,34 @@ public class Address extends AbstractTypedData { - public static final String ELEMENT_NAME = "ADR"; + public static final String ELEMENT_NAME = "ADR"; - protected final Map properties; + protected final Map properties; - public Address() { - super(); - properties = new HashMap(); - } + public Address() { + super(); + properties = new HashMap(); + } - public Map getProperties() { - return properties; - } + public Map getProperties() { + return properties; + } - @Override - protected String getElementName() { - return ELEMENT_NAME; - } + @Override + protected String getElementName() { + return ELEMENT_NAME; + } - @Override - protected void writeBody(XmlSerializer serializer) throws IOException { - for (Entry entry : properties.entrySet()) - SerializerUtils.addTextTag(serializer, entry.getKey().toString(), - entry.getValue()); - } + @Override + protected void writeBody(XmlSerializer serializer) throws IOException { + for (Entry entry : properties.entrySet()) + SerializerUtils.addTextTag(serializer, entry.getKey().toString(), + entry.getValue()); + } - @Override - public boolean isValid() { - return AddressType.isValid(types); - } + @Override + public boolean isValid() { + return AddressType.isValid(types); + } } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/AddressProperty.java b/app/src/main/java/com/xabber/xmpp/vcard/AddressProperty.java index b710d04547..8ec257449a 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/AddressProperty.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/AddressProperty.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,18 +16,18 @@ public enum AddressProperty { - POBOX, + POBOX, - EXTADR, + EXTADR, - STREET, + STREET, - LOCALITY, + LOCALITY, - REGION, + REGION, - PCODE, + PCODE, - CTRY; + CTRY } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/AddressProvider.java b/app/src/main/java/com/xabber/xmpp/vcard/AddressProvider.java index 1179d807d8..77b48a8e9d 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/AddressProvider.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/AddressProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,38 +20,38 @@ class AddressProvider extends AbstractTypedDataProvider { - @Override - protected boolean parseInner(XmlPullParser parser, Address instance) - throws Exception { - if (super.parseInner(parser, instance)) - return true; - String name = parser.getName(); - for (AddressProperty key : AddressProperty.values()) - if (key.toString().equals(name)) { - instance.getProperties().put(key, - ProviderUtils.parseText(parser)); - return true; - } - return false; - } - - @Override - protected AddressType[] getTypes() { - return AddressType.values(); - } - - @Override - protected Address createInstance(XmlPullParser parser) { - return new Address(); - } - - private AddressProvider() { - } - - private static final AddressProvider instance = new AddressProvider(); - - public static AddressProvider getInstance() { - return instance; - } + @Override + protected boolean parseInner(XmlPullParser parser, Address instance) + throws Exception { + if (super.parseInner(parser, instance)) + return true; + String name = parser.getName(); + for (AddressProperty key : AddressProperty.values()) + if (key.toString().equals(name)) { + instance.getProperties().put(key, + ProviderUtils.parseText(parser)); + return true; + } + return false; + } + + @Override + protected AddressType[] getTypes() { + return AddressType.values(); + } + + @Override + protected Address createInstance(XmlPullParser parser) { + return new Address(); + } + + private AddressProvider() { + } + + private static final AddressProvider instance = new AddressProvider(); + + public static AddressProvider getInstance() { + return instance; + } } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/AddressType.java b/app/src/main/java/com/xabber/xmpp/vcard/AddressType.java index 853cdad997..284b9e991e 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/AddressType.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/AddressType.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,22 +18,22 @@ public enum AddressType { - HOME, + HOME, - WORK, + WORK, - POSTAL, + POSTAL, - PARCEL, + PARCEL, - DOM, + DOM, - INTL, + INTL, - PREF; + PREF; - public static boolean isValid(Set set) { - return !(set.contains(DOM) && set.contains(INTL)); - } + public static boolean isValid(Set set) { + return !(set.contains(DOM) && set.contains(INTL)); + } } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/BinaryLogo.java b/app/src/main/java/com/xabber/xmpp/vcard/BinaryLogo.java index 8f7f001ded..8d197cdd87 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/BinaryLogo.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/BinaryLogo.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,9 +16,9 @@ public class BinaryLogo extends AbstractBinaryData implements Logo { - @Override - public String getElementName() { - return Logo.ELEMENT_NAME; - } + @Override + public String getElementName() { + return Logo.ELEMENT_NAME; + } } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/BinaryPhoto.java b/app/src/main/java/com/xabber/xmpp/vcard/BinaryPhoto.java index 713e9df0ed..8482951320 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/BinaryPhoto.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/BinaryPhoto.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,9 +16,9 @@ public class BinaryPhoto extends AbstractBinaryData implements Photo { - @Override - public String getElementName() { - return Photo.ELEMENT_NAME; - } + @Override + public String getElementName() { + return Photo.ELEMENT_NAME; + } } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/BinarySound.java b/app/src/main/java/com/xabber/xmpp/vcard/BinarySound.java index bea5f34a79..80213cf7d3 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/BinarySound.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/BinarySound.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,9 +16,9 @@ public class BinarySound extends AbstractBinaryData implements Sound { - @Override - public String getElementName() { - return Sound.ELEMENT_NAME; - } + @Override + public String getElementName() { + return Sound.ELEMENT_NAME; + } } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/CategoriesInflater.java b/app/src/main/java/com/xabber/xmpp/vcard/CategoriesInflater.java index cbada5ea76..4a87ac827a 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/CategoriesInflater.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/CategoriesInflater.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -21,25 +21,25 @@ class CategoriesInflater extends AbstractInflater { - @Override - protected boolean parseInner(XmlPullParser parser, VCard instance) - throws Exception { - if (super.parseInner(parser, instance)) - return true; - if (VCard.KEYWORD_NAME.equals(parser.getName())) - instance.getCategories().add(ProviderUtils.parseText(parser)); - else - return false; - return true; - } - - private CategoriesInflater() { - } - - private static final CategoriesInflater instance = new CategoriesInflater(); - - public static CategoriesInflater getInstance() { - return instance; - } + @Override + protected boolean parseInner(XmlPullParser parser, VCard instance) + throws Exception { + if (super.parseInner(parser, instance)) + return true; + if (VCard.KEYWORD_NAME.equals(parser.getName())) + instance.getCategories().add(ProviderUtils.parseText(parser)); + else + return false; + return true; + } + + private CategoriesInflater() { + } + + private static final CategoriesInflater instance = new CategoriesInflater(); + + public static CategoriesInflater getInstance() { + return instance; + } } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/Classification.java b/app/src/main/java/com/xabber/xmpp/vcard/Classification.java index d931b20e8f..87b5f707a8 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/Classification.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/Classification.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,10 +16,10 @@ public enum Classification { - PUBLIC, + PUBLIC, - PRIVATE, + PRIVATE, - CONFIDENTIAL; + CONFIDENTIAL } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/ClassificationInflater.java b/app/src/main/java/com/xabber/xmpp/vcard/ClassificationInflater.java index 3822ad19ed..23c028b089 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/ClassificationInflater.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/ClassificationInflater.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -21,28 +21,28 @@ class ClassificationInflater extends AbstractInflater { - @Override - protected boolean parseInner(XmlPullParser parser, VCard instance) - throws Exception { - if (super.parseInner(parser, instance)) - return true; - String name = parser.getName(); - for (Classification value : Classification.values()) - if (value.toString().equals(name)) { - instance.setClassification(value); - ProviderUtils.skipTag(parser); - return true; - } - return false; - } - - private ClassificationInflater() { - } - - private static final ClassificationInflater instance = new ClassificationInflater(); - - public static ClassificationInflater getInstance() { - return instance; - } + @Override + protected boolean parseInner(XmlPullParser parser, VCard instance) + throws Exception { + if (super.parseInner(parser, instance)) + return true; + String name = parser.getName(); + for (Classification value : Classification.values()) + if (value.toString().equals(name)) { + instance.setClassification(value); + ProviderUtils.skipTag(parser); + return true; + } + return false; + } + + private ClassificationInflater() { + } + + private static final ClassificationInflater instance = new ClassificationInflater(); + + public static ClassificationInflater getInstance() { + return instance; + } } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/DataHolder.java b/app/src/main/java/com/xabber/xmpp/vcard/DataHolder.java index f9bc34ad49..ca540c34dd 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/DataHolder.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/DataHolder.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -22,32 +22,31 @@ /** * Holder for the some object. - * - * @author alexander.ivanov - * + * * @param + * @author alexander.ivanov */ class DataHolder implements Instance { - private T payload; + private T payload; - @Override - public boolean isValid() { - return payload != null && payload.isValid(); - } + @Override + public boolean isValid() { + return payload != null && payload.isValid(); + } - public T getPayload() { - return payload; - } + public T getPayload() { + return payload; + } - public void setPayload(T payload) { - this.payload = payload; - } + public void setPayload(T payload) { + this.payload = payload; + } - @Override - public void serialize(XmlSerializer serializer) throws IOException { - // TODO: - throw new UnsupportedOperationException(); - } + @Override + public void serialize(XmlSerializer serializer) throws IOException { + // TODO: + throw new UnsupportedOperationException(); + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/xmpp/vcard/Email.java b/app/src/main/java/com/xabber/xmpp/vcard/Email.java index 9d55bc28aa..15e32185b0 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/Email.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/Email.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,21 +16,21 @@ public class Email extends AbstractTypedDataWithValue { - public static final String ELEMENT_NAME = "TEL"; - public static final String USERID_NAME = "USERID"; + public static final String ELEMENT_NAME = "EMAIL"; + public static final String USERID_NAME = "USERID"; - public Email() { - super(); - } + public Email() { + super(); + } - @Override - protected String getElementName() { - return ELEMENT_NAME; - } + @Override + protected String getElementName() { + return ELEMENT_NAME; + } - @Override - protected String getValueName() { - return USERID_NAME; - } + @Override + protected String getValueName() { + return USERID_NAME; + } } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/EmailProvider.java b/app/src/main/java/com/xabber/xmpp/vcard/EmailProvider.java index 169edd14ac..b10fbe0ebd 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/EmailProvider.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/EmailProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -17,30 +17,30 @@ import org.xmlpull.v1.XmlPullParser; class EmailProvider extends - AbstractTypedDataWithValueProvider { + AbstractTypedDataWithValueProvider { - @Override - protected String getValueName() { - return Email.USERID_NAME; - } + @Override + protected String getValueName() { + return Email.USERID_NAME; + } - @Override - protected EmailType[] getTypes() { - return EmailType.values(); - } + @Override + protected EmailType[] getTypes() { + return EmailType.values(); + } - @Override - protected Email createInstance(XmlPullParser parser) { - return new Email(); - } + @Override + protected Email createInstance(XmlPullParser parser) { + return new Email(); + } - private EmailProvider() { - } + private EmailProvider() { + } - private static final EmailProvider instance = new EmailProvider(); + private static final EmailProvider instance = new EmailProvider(); - public static EmailProvider getInstance() { - return instance; - } + public static EmailProvider getInstance() { + return instance; + } } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/EmailType.java b/app/src/main/java/com/xabber/xmpp/vcard/EmailType.java index 38b8347e81..e5ce58df37 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/EmailType.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/EmailType.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,14 +16,14 @@ public enum EmailType { - HOME, + HOME, - WORK, + WORK, - INTERNET, + INTERNET, - X400, + X400, - PREF; + PREF } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/ExternalLogo.java b/app/src/main/java/com/xabber/xmpp/vcard/ExternalLogo.java index ae0081ca5f..c1cebf5868 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/ExternalLogo.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/ExternalLogo.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,9 +16,9 @@ public class ExternalLogo extends AbstractExternalData implements Logo { - @Override - public String getElementName() { - return Logo.ELEMENT_NAME; - } + @Override + public String getElementName() { + return Logo.ELEMENT_NAME; + } } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/ExternalPhoto.java b/app/src/main/java/com/xabber/xmpp/vcard/ExternalPhoto.java index 4d1af147a8..898b095164 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/ExternalPhoto.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/ExternalPhoto.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,9 +16,9 @@ public class ExternalPhoto extends AbstractExternalData implements Photo { - @Override - public String getElementName() { - return Photo.ELEMENT_NAME; - } + @Override + public String getElementName() { + return Photo.ELEMENT_NAME; + } } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/ExternalSound.java b/app/src/main/java/com/xabber/xmpp/vcard/ExternalSound.java index 329c55a5f5..7644e0015d 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/ExternalSound.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/ExternalSound.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,9 +16,9 @@ public class ExternalSound extends AbstractExternalData implements Sound { - @Override - public String getElementName() { - return Sound.ELEMENT_NAME; - } + @Override + public String getElementName() { + return Sound.ELEMENT_NAME; + } } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/Geo.java b/app/src/main/java/com/xabber/xmpp/vcard/Geo.java index df718ff5af..095e1612b3 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/Geo.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/Geo.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -23,40 +23,40 @@ public class Geo implements Instance { - public static final String ELEMENT_NAME = "GEO"; - public static final String LAT_NAME = "LAT"; - public static final String LON_NAME = "LON"; - - private String lat; - private String lon; - - public String getLat() { - return lat; - } - - public void setLat(String lat) { - this.lat = lat; - } - - public String getLon() { - return lon; - } - - public void setLon(String lon) { - this.lon = lon; - } - - @Override - public boolean isValid() { - return lat != null && lon != null; - } - - @Override - public void serialize(XmlSerializer serializer) throws IOException { - serializer.startTag(null, ELEMENT_NAME); - SerializerUtils.addTextTag(serializer, LAT_NAME, lat); - SerializerUtils.addTextTag(serializer, LON_NAME, lon); - serializer.endTag(null, ELEMENT_NAME); - } + public static final String ELEMENT_NAME = "GEO"; + public static final String LAT_NAME = "LAT"; + public static final String LON_NAME = "LON"; + + private String lat; + private String lon; + + public String getLat() { + return lat; + } + + public void setLat(String lat) { + this.lat = lat; + } + + public String getLon() { + return lon; + } + + public void setLon(String lon) { + this.lon = lon; + } + + @Override + public boolean isValid() { + return lat != null && lon != null; + } + + @Override + public void serialize(XmlSerializer serializer) throws IOException { + serializer.startTag(null, ELEMENT_NAME); + SerializerUtils.addTextTag(serializer, LAT_NAME, lat); + SerializerUtils.addTextTag(serializer, LON_NAME, lon); + serializer.endTag(null, ELEMENT_NAME); + } } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/GeoProvider.java b/app/src/main/java/com/xabber/xmpp/vcard/GeoProvider.java index 96c54ca516..a0b9c4e896 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/GeoProvider.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/GeoProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -21,32 +21,32 @@ class GeoProvider extends AbstractProvider { - @Override - protected boolean parseInner(XmlPullParser parser, Geo instance) - throws Exception { - if (super.parseInner(parser, instance)) - return true; - if (Geo.LAT_NAME.equals(parser.getName())) - instance.setLat(ProviderUtils.parseText(parser)); - else if (Geo.LON_NAME.equals(parser.getName())) - instance.setLon(ProviderUtils.parseText(parser)); - else - return false; - return true; - } - - @Override - protected Geo createInstance(XmlPullParser parser) { - return new Geo(); - } - - private GeoProvider() { - } - - private static final GeoProvider instance = new GeoProvider(); - - public static GeoProvider getInstance() { - return instance; - } + @Override + protected boolean parseInner(XmlPullParser parser, Geo instance) + throws Exception { + if (super.parseInner(parser, instance)) + return true; + if (Geo.LAT_NAME.equals(parser.getName())) + instance.setLat(ProviderUtils.parseText(parser)); + else if (Geo.LON_NAME.equals(parser.getName())) + instance.setLon(ProviderUtils.parseText(parser)); + else + return false; + return true; + } + + @Override + protected Geo createInstance(XmlPullParser parser) { + return new Geo(); + } + + private GeoProvider() { + } + + private static final GeoProvider instance = new GeoProvider(); + + public static GeoProvider getInstance() { + return instance; + } } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/Key.java b/app/src/main/java/com/xabber/xmpp/vcard/Key.java index 722cf4e68a..941581c719 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/Key.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/Key.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -23,46 +23,46 @@ public class Key implements Instance { - /** - * Limit received encoded data size. - */ - public static final int MAX_ENCODED_DATA_SIZE = 64 * 1024; + /** + * Limit received encoded data size. + */ + public static final int MAX_ENCODED_DATA_SIZE = 64 * 1024; - public static final String ELEMENT_NAME = "KEY"; - public static final String TYPE_NAME = "TYPE"; - public static final String CRED_NAME = "CRED"; + public static final String ELEMENT_NAME = "KEY"; + public static final String TYPE_NAME = "TYPE"; + public static final String CRED_NAME = "CRED"; - private String type; - private String encodedData; + private String type; + private String encodedData; - public String getType() { - return type; - } + public String getType() { + return type; + } - public void setType(String type) { - this.type = type; - } + public void setType(String type) { + this.type = type; + } - public String getEncodedData() { - return encodedData; - } + public String getEncodedData() { + return encodedData; + } - public void setEncodedData(String encodedData) { - this.encodedData = encodedData; - } + public void setEncodedData(String encodedData) { + this.encodedData = encodedData; + } - @Override - public boolean isValid() { - return encodedData != null; - } + @Override + public boolean isValid() { + return encodedData != null; + } - @Override - public void serialize(XmlSerializer serializer) throws IOException { - serializer.startTag(null, ELEMENT_NAME); - if (type != null) - SerializerUtils.addTextTag(serializer, TYPE_NAME, type); - SerializerUtils.addTextTag(serializer, CRED_NAME, encodedData); - serializer.endTag(null, ELEMENT_NAME); - } + @Override + public void serialize(XmlSerializer serializer) throws IOException { + serializer.startTag(null, ELEMENT_NAME); + if (type != null) + SerializerUtils.addTextTag(serializer, TYPE_NAME, type); + SerializerUtils.addTextTag(serializer, CRED_NAME, encodedData); + serializer.endTag(null, ELEMENT_NAME); + } } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/KeyProvider.java b/app/src/main/java/com/xabber/xmpp/vcard/KeyProvider.java index 4ac51cf3c7..918d7acc43 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/KeyProvider.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/KeyProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -22,39 +22,39 @@ class KeyProvider extends AbstractProvider { - @Override - protected boolean parseInner(XmlPullParser parser, Key instance) - throws Exception { - if (super.parseInner(parser, instance)) - return true; - if (Key.TYPE_NAME.equals(parser.getName())) - instance.setType(ProviderUtils.parseText(parser)); - else if (Key.CRED_NAME.equals(parser.getName())) { - String value; - try { - value = ProviderUtils.parseText(parser, - Key.MAX_ENCODED_DATA_SIZE); - } catch (OverflowReceiverBufferException e) { - return true; - } - instance.setEncodedData(value); - } else - return false; - return true; - } - - @Override - protected Key createInstance(XmlPullParser parser) { - return new Key(); - } - - private KeyProvider() { - } - - private static final KeyProvider instance = new KeyProvider(); - - public static KeyProvider getInstance() { - return instance; - } + @Override + protected boolean parseInner(XmlPullParser parser, Key instance) + throws Exception { + if (super.parseInner(parser, instance)) + return true; + if (Key.TYPE_NAME.equals(parser.getName())) + instance.setType(ProviderUtils.parseText(parser)); + else if (Key.CRED_NAME.equals(parser.getName())) { + String value; + try { + value = ProviderUtils.parseText(parser, + Key.MAX_ENCODED_DATA_SIZE); + } catch (OverflowReceiverBufferException e) { + return true; + } + instance.setEncodedData(value); + } else + return false; + return true; + } + + @Override + protected Key createInstance(XmlPullParser parser) { + return new Key(); + } + + private KeyProvider() { + } + + private static final KeyProvider instance = new KeyProvider(); + + public static KeyProvider getInstance() { + return instance; + } } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/Label.java b/app/src/main/java/com/xabber/xmpp/vcard/Label.java index 1ad07e1523..effaa9fea4 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/Label.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/Label.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -24,34 +24,34 @@ public class Label extends AbstractTypedData { - public static final String ELEMENT_NAME = "LABEL"; - public static final String LINE_NAME = "LINE"; + public static final String ELEMENT_NAME = "LABEL"; + public static final String LINE_NAME = "LINE"; - private final List lines; + private final List lines; - public Label() { - super(); - lines = new ArrayList(); - } + public Label() { + super(); + lines = new ArrayList(); + } - public List getLines() { - return lines; - } + public List getLines() { + return lines; + } - @Override - protected String getElementName() { - return ELEMENT_NAME; - } + @Override + protected String getElementName() { + return ELEMENT_NAME; + } - @Override - protected void writeBody(XmlSerializer serializer) throws IOException { - for (String line : lines) - SerializerUtils.addTextTag(serializer, LINE_NAME, line); - } + @Override + protected void writeBody(XmlSerializer serializer) throws IOException { + for (String line : lines) + SerializerUtils.addTextTag(serializer, LINE_NAME, line); + } - @Override - public boolean isValid() { - return AddressType.isValid(types) && !lines.isEmpty(); - } + @Override + public boolean isValid() { + return AddressType.isValid(types) && !lines.isEmpty(); + } } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/LabelProvider.java b/app/src/main/java/com/xabber/xmpp/vcard/LabelProvider.java index 9e6c990752..f9b3eb2957 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/LabelProvider.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/LabelProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -20,35 +20,35 @@ class LabelProvider extends AbstractTypedDataProvider { - @Override - protected boolean parseInner(XmlPullParser parser, Label instance) - throws Exception { - if (super.parseInner(parser, instance)) - return true; - if (Label.LINE_NAME.equals(parser.getName())) { - instance.getLines().add(ProviderUtils.parseText(parser)); - return true; - } - return false; - } - - @Override - protected AddressType[] getTypes() { - return AddressType.values(); - } - - @Override - protected Label createInstance(XmlPullParser parser) { - return new Label(); - } - - private LabelProvider() { - } - - private static final LabelProvider instance = new LabelProvider(); - - public static LabelProvider getInstance() { - return instance; - } + @Override + protected boolean parseInner(XmlPullParser parser, Label instance) + throws Exception { + if (super.parseInner(parser, instance)) + return true; + if (Label.LINE_NAME.equals(parser.getName())) { + instance.getLines().add(ProviderUtils.parseText(parser)); + return true; + } + return false; + } + + @Override + protected AddressType[] getTypes() { + return AddressType.values(); + } + + @Override + protected Label createInstance(XmlPullParser parser) { + return new Label(); + } + + private LabelProvider() { + } + + private static final LabelProvider instance = new LabelProvider(); + + public static LabelProvider getInstance() { + return instance; + } } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/Logo.java b/app/src/main/java/com/xabber/xmpp/vcard/Logo.java index dae8c32bf2..add282d031 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/Logo.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/Logo.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,6 +18,6 @@ public interface Logo extends Instance { - public static final String ELEMENT_NAME = "LOGO"; + String ELEMENT_NAME = "LOGO"; } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/LogoHolderProvider.java b/app/src/main/java/com/xabber/xmpp/vcard/LogoHolderProvider.java index d71a50bdb0..5c41a44e27 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/LogoHolderProvider.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/LogoHolderProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,28 +18,28 @@ class LogoHolderProvider extends AbstractDataProvider> { - @Override - protected Logo createBinaryData() { - return new BinaryLogo(); - } + @Override + protected Logo createBinaryData() { + return new BinaryLogo(); + } - @Override - protected Logo createExternalData() { - return new ExternalLogo(); - } + @Override + protected Logo createExternalData() { + return new ExternalLogo(); + } - @Override - protected DataHolder createInstance(XmlPullParser parser) { - return new DataHolder(); - } + @Override + protected DataHolder createInstance(XmlPullParser parser) { + return new DataHolder(); + } - private LogoHolderProvider() { - } + private LogoHolderProvider() { + } - private static final LogoHolderProvider instance = new LogoHolderProvider(); + private static final LogoHolderProvider instance = new LogoHolderProvider(); - public static LogoHolderProvider getInstance() { - return instance; - } + public static LogoHolderProvider getInstance() { + return instance; + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/xmpp/vcard/NameInflater.java b/app/src/main/java/com/xabber/xmpp/vcard/NameInflater.java index 2695cd40ba..f1f738120a 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/NameInflater.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/NameInflater.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -21,27 +21,27 @@ class NameInflater extends AbstractInflater { - @Override - protected boolean parseInner(XmlPullParser parser, VCard instance) - throws Exception { - if (super.parseInner(parser, instance)) - return true; - String name = parser.getName(); - for (NameProperty key : NameProperty.values()) - if (key.toString().equals(name)) { - instance.getName().put(key, ProviderUtils.parseText(parser)); - return true; - } - return false; - } - - private NameInflater() { - } - - private static final NameInflater instance = new NameInflater(); - - public static NameInflater getInstance() { - return instance; - } + @Override + protected boolean parseInner(XmlPullParser parser, VCard instance) + throws Exception { + if (super.parseInner(parser, instance)) + return true; + String name = parser.getName(); + for (NameProperty key : NameProperty.values()) + if (key.toString().equals(name)) { + instance.getName().put(key, ProviderUtils.parseText(parser)); + return true; + } + return false; + } + + private NameInflater() { + } + + private static final NameInflater instance = new NameInflater(); + + public static NameInflater getInstance() { + return instance; + } } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/NameProperty.java b/app/src/main/java/com/xabber/xmpp/vcard/NameProperty.java index 8692c561a5..ef657f506f 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/NameProperty.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/NameProperty.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,14 +16,14 @@ public enum NameProperty { - FAMILY, + FAMILY, - GIVEN, + GIVEN, - MIDDLE, + MIDDLE, - PREFIX, + PREFIX, - SUFFIX; + SUFFIX } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/Organization.java b/app/src/main/java/com/xabber/xmpp/vcard/Organization.java index 30059f60e4..bf33674aa3 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/Organization.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/Organization.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -25,41 +25,41 @@ public class Organization implements Instance { - public static final String ELEMENT_NAME = "ORG"; - public static final String ORGNAME_NAME = "ORGNAME"; - public static final String ORGUNIT_NAME = "ORGUNIT"; + public static final String ELEMENT_NAME = "ORG"; + public static final String ORGNAME_NAME = "ORGNAME"; + public static final String ORGUNIT_NAME = "ORGUNIT"; - private String name; - private final List units; + private String name; + private final List units; - public Organization() { - units = new ArrayList(); - } + public Organization() { + units = new ArrayList(); + } - public String getName() { - return name; - } + public String getName() { + return name; + } - public void setName(String name) { - this.name = name; - } + public void setName(String name) { + this.name = name; + } - public List getUnits() { - return units; - } + public List getUnits() { + return units; + } - @Override - public boolean isValid() { - return name != null; - } + @Override + public boolean isValid() { + return name != null; + } - @Override - public void serialize(XmlSerializer serializer) throws IOException { - serializer.startTag(null, ELEMENT_NAME); - SerializerUtils.addTextTag(serializer, ORGNAME_NAME, name); - for (String unit : units) - SerializerUtils.addTextTag(serializer, ORGUNIT_NAME, unit); - serializer.endTag(null, ELEMENT_NAME); - } + @Override + public void serialize(XmlSerializer serializer) throws IOException { + serializer.startTag(null, ELEMENT_NAME); + SerializerUtils.addTextTag(serializer, ORGNAME_NAME, name); + for (String unit : units) + SerializerUtils.addTextTag(serializer, ORGUNIT_NAME, unit); + serializer.endTag(null, ELEMENT_NAME); + } } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/OrganizationProvider.java b/app/src/main/java/com/xabber/xmpp/vcard/OrganizationProvider.java index 9ba0dacc01..df221f646c 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/OrganizationProvider.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/OrganizationProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -21,32 +21,32 @@ class OrganizationProvider extends AbstractProvider { - @Override - protected boolean parseInner(XmlPullParser parser, Organization instance) - throws Exception { - if (super.parseInner(parser, instance)) - return true; - if (Organization.ORGNAME_NAME.equals(parser.getName())) - instance.setName(ProviderUtils.parseText(parser)); - else if (Organization.ORGUNIT_NAME.equals(parser.getName())) - instance.getUnits().add(ProviderUtils.parseText(parser)); - else - return false; - return true; - } - - @Override - protected Organization createInstance(XmlPullParser parser) { - return new Organization(); - } - - private OrganizationProvider() { - } - - private static final OrganizationProvider instance = new OrganizationProvider(); - - public static OrganizationProvider getInstance() { - return instance; - } + @Override + protected boolean parseInner(XmlPullParser parser, Organization instance) + throws Exception { + if (super.parseInner(parser, instance)) + return true; + if (Organization.ORGNAME_NAME.equals(parser.getName())) + instance.setName(ProviderUtils.parseText(parser)); + else if (Organization.ORGUNIT_NAME.equals(parser.getName())) + instance.getUnits().add(ProviderUtils.parseText(parser)); + else + return false; + return true; + } + + @Override + protected Organization createInstance(XmlPullParser parser) { + return new Organization(); + } + + private OrganizationProvider() { + } + + private static final OrganizationProvider instance = new OrganizationProvider(); + + public static OrganizationProvider getInstance() { + return instance; + } } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/PhoneticSound.java b/app/src/main/java/com/xabber/xmpp/vcard/PhoneticSound.java index af5768c8f3..7aaa730eec 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/PhoneticSound.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/PhoneticSound.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -22,31 +22,31 @@ public class PhoneticSound extends AbstractData implements Sound { - public static final String PHONETIC_NAME = "PHONETIC"; + public static final String PHONETIC_NAME = "PHONETIC"; - private String value; + private String value; - public String getValue() { - return value; - } + public String getValue() { + return value; + } - public void setValue(String value) { - this.value = value; - } + public void setValue(String value) { + this.value = value; + } - @Override - public boolean isValid() { - return value != null; - } + @Override + public boolean isValid() { + return value != null; + } - @Override - protected void writeBody(XmlSerializer serializer) throws IOException { - SerializerUtils.addTextTag(serializer, PHONETIC_NAME, value); - } + @Override + protected void writeBody(XmlSerializer serializer) throws IOException { + SerializerUtils.addTextTag(serializer, PHONETIC_NAME, value); + } - @Override - public String getElementName() { - return Sound.ELEMENT_NAME; - } + @Override + public String getElementName() { + return Sound.ELEMENT_NAME; + } } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/Photo.java b/app/src/main/java/com/xabber/xmpp/vcard/Photo.java index 88b18a6872..3543c30b97 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/Photo.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/Photo.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,6 +18,6 @@ public interface Photo extends Instance { - public static final String ELEMENT_NAME = "PHOTO"; + String ELEMENT_NAME = "PHOTO"; } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/PhotoHolderProvider.java b/app/src/main/java/com/xabber/xmpp/vcard/PhotoHolderProvider.java index ede5a4ec0b..3ca725ec55 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/PhotoHolderProvider.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/PhotoHolderProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -17,30 +17,30 @@ import org.xmlpull.v1.XmlPullParser; class PhotoHolderProvider extends - AbstractDataProvider> { + AbstractDataProvider> { - @Override - protected Photo createBinaryData() { - return new BinaryPhoto(); - } + @Override + protected Photo createBinaryData() { + return new BinaryPhoto(); + } - @Override - protected Photo createExternalData() { - return new ExternalPhoto(); - } + @Override + protected Photo createExternalData() { + return new ExternalPhoto(); + } - @Override - protected DataHolder createInstance(XmlPullParser parser) { - return new DataHolder(); - } + @Override + protected DataHolder createInstance(XmlPullParser parser) { + return new DataHolder(); + } - private PhotoHolderProvider() { - } + private PhotoHolderProvider() { + } - private static final PhotoHolderProvider instance = new PhotoHolderProvider(); + private static final PhotoHolderProvider instance = new PhotoHolderProvider(); - public static PhotoHolderProvider getInstance() { - return instance; - } + public static PhotoHolderProvider getInstance() { + return instance; + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/xmpp/vcard/Sound.java b/app/src/main/java/com/xabber/xmpp/vcard/Sound.java index 20f999569e..27fd710691 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/Sound.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/Sound.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -18,6 +18,6 @@ public interface Sound extends Instance { - public static final String ELEMENT_NAME = "SOUND"; + String ELEMENT_NAME = "SOUND"; } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/SoundHolderProvider.java b/app/src/main/java/com/xabber/xmpp/vcard/SoundHolderProvider.java index e7277c3970..28ee50938c 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/SoundHolderProvider.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/SoundHolderProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -19,67 +19,67 @@ import com.xabber.xmpp.ProviderUtils; class SoundHolderProvider extends - AbstractDataProvider> { + AbstractDataProvider> { - @Override - protected boolean createPayload(XmlPullParser parser, - DataHolder instance) throws Exception { - if (super.createPayload(parser, instance)) - return true; - if (PhoneticSound.PHONETIC_NAME.equals(parser.getName())) - instance.setPayload(createPhoneticSound()); - else - return false; - return true; - } + @Override + protected boolean createPayload(XmlPullParser parser, + DataHolder instance) throws Exception { + if (super.createPayload(parser, instance)) + return true; + if (PhoneticSound.PHONETIC_NAME.equals(parser.getName())) + instance.setPayload(createPhoneticSound()); + else + return false; + return true; + } - @Override - protected Sound createBinaryData() { - return new BinarySound(); - } + @Override + protected Sound createBinaryData() { + return new BinarySound(); + } - @Override - protected Sound createExternalData() { - return new ExternalSound(); - } + @Override + protected Sound createExternalData() { + return new ExternalSound(); + } - protected Sound createPhoneticSound() { - return new PhoneticSound(); - } + protected Sound createPhoneticSound() { + return new PhoneticSound(); + } - @Override - protected boolean inflatePayload(XmlPullParser parser, - DataHolder instance) throws Exception { - if (super.inflatePayload(parser, instance)) - return true; - if (instance.getPayload() instanceof PhoneticSound) - return inflatePhoneticSound(parser, - (PhoneticSound) instance.getPayload()); - else - return false; - } + @Override + protected boolean inflatePayload(XmlPullParser parser, + DataHolder instance) throws Exception { + if (super.inflatePayload(parser, instance)) + return true; + if (instance.getPayload() instanceof PhoneticSound) + return inflatePhoneticSound(parser, + (PhoneticSound) instance.getPayload()); + else + return false; + } - protected boolean inflatePhoneticSound(XmlPullParser parser, - PhoneticSound payload) throws Exception { - if (PhoneticSound.PHONETIC_NAME.equals(parser.getName())) - payload.setValue(ProviderUtils.parseText(parser)); - else - return false; - return true; - } + protected boolean inflatePhoneticSound(XmlPullParser parser, + PhoneticSound payload) throws Exception { + if (PhoneticSound.PHONETIC_NAME.equals(parser.getName())) + payload.setValue(ProviderUtils.parseText(parser)); + else + return false; + return true; + } - @Override - protected DataHolder createInstance(XmlPullParser parser) { - return new DataHolder(); - } + @Override + protected DataHolder createInstance(XmlPullParser parser) { + return new DataHolder(); + } - private SoundHolderProvider() { - } + private SoundHolderProvider() { + } - private static final SoundHolderProvider instance = new SoundHolderProvider(); + private static final SoundHolderProvider instance = new SoundHolderProvider(); - public static SoundHolderProvider getInstance() { - return instance; - } + public static SoundHolderProvider getInstance() { + return instance; + } } \ No newline at end of file diff --git a/app/src/main/java/com/xabber/xmpp/vcard/Telephone.java b/app/src/main/java/com/xabber/xmpp/vcard/Telephone.java index a97b1951f7..990dd25252 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/Telephone.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/Telephone.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -17,21 +17,21 @@ public class Telephone extends AbstractTypedDataWithValue { - public static final String ELEMENT_NAME = "TEL"; - public static final String NUMBER_NAME = "NUMBER"; + public static final String ELEMENT_NAME = "TEL"; + public static final String NUMBER_NAME = "NUMBER"; - public Telephone() { - super(); - } + public Telephone() { + super(); + } - @Override - protected String getElementName() { - return ELEMENT_NAME; - } + @Override + protected String getElementName() { + return ELEMENT_NAME; + } - @Override - protected String getValueName() { - return NUMBER_NAME; - } + @Override + protected String getValueName() { + return NUMBER_NAME; + } } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/TelephoneProvider.java b/app/src/main/java/com/xabber/xmpp/vcard/TelephoneProvider.java index df96d4e17e..758475e051 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/TelephoneProvider.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/TelephoneProvider.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -17,30 +17,30 @@ import org.xmlpull.v1.XmlPullParser; class TelephoneProvider extends - AbstractTypedDataWithValueProvider { + AbstractTypedDataWithValueProvider { - @Override - protected String getValueName() { - return Telephone.NUMBER_NAME; - } + @Override + protected String getValueName() { + return Telephone.NUMBER_NAME; + } - @Override - protected TelephoneType[] getTypes() { - return TelephoneType.values(); - } + @Override + protected TelephoneType[] getTypes() { + return TelephoneType.values(); + } - @Override - protected Telephone createInstance(XmlPullParser parser) { - return new Telephone(); - } + @Override + protected Telephone createInstance(XmlPullParser parser) { + return new Telephone(); + } - private TelephoneProvider() { - } + private TelephoneProvider() { + } - private static final TelephoneProvider instance = new TelephoneProvider(); + private static final TelephoneProvider instance = new TelephoneProvider(); - public static TelephoneProvider getInstance() { - return instance; - } + public static TelephoneProvider getInstance() { + return instance; + } } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/TelephoneType.java b/app/src/main/java/com/xabber/xmpp/vcard/TelephoneType.java index 52a309da7d..3634c48f75 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/TelephoneType.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/TelephoneType.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -16,30 +16,30 @@ public enum TelephoneType { - HOME, + HOME, - WORK, + WORK, - VOICE, + VOICE, - FAX, + FAX, - PAGER, + PAGER, - MSG, + MSG, - CELL, + CELL, - VIDEO, + VIDEO, - BBS, + BBS, - MODEM, + MODEM, - ISDN, + ISDN, - PCS, + PCS, - PREF; + PREF } diff --git a/app/src/main/java/com/xabber/xmpp/vcard/VCard.java b/app/src/main/java/com/xabber/xmpp/vcard/VCard.java index 2ff172dbe0..d33f59a093 100644 --- a/app/src/main/java/com/xabber/xmpp/vcard/VCard.java +++ b/app/src/main/java/com/xabber/xmpp/vcard/VCard.java @@ -1,14 +1,14 @@ /** * Copyright (c) 2013, Redsolution LTD. All rights reserved. - * + * * This file is part of Xabber project; you can redistribute it and/or * modify it under the terms of the GNU General Public License, Version 3. - * + * * Xabber is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License, * along with this program. If not, see http://www.gnu.org/licenses/. */ @@ -31,268 +31,267 @@ /** * vCard-temp - * + *

* http://xmpp.org/extensions/xep-0054.html - * + * * @author alexander.ivanov - * */ public class VCard extends IQ { - public static final String ELEMENT_NAME = "vCard"; - public static final String NAMESPACE = "vcard-temp"; - - public static final String N_NAME = "N"; - public static final String CLASS_NAME = "CLASS"; - public static final String CATEGORIES_NAME = "CATEGORIES"; - public static final String KEYWORD_NAME = "KEYWORD"; - - private String version; - private final Map name; - private final Map properties; - private final List photos; - private final List

addresses; - private final List