Merge branch 'release/1.0-beta18'
This commit is contained in:
commit
396f1a23a6
107
app/build.gradle
107
app/build.gradle
@ -1,32 +1,36 @@
|
||||
apply plugin: 'idea'
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'idea'
|
||||
|
||||
ext {
|
||||
appName = "Abit"
|
||||
}
|
||||
if (project.hasProperty("project.configs")
|
||||
&& new File(project.property("project.configs") + appName + ".gradle").exists()) {
|
||||
apply from: project.property("project.configs") + appName + ".gradle";
|
||||
apply from: project.property("project.configs") + appName + ".gradle"
|
||||
}
|
||||
|
||||
//noinspection GroovyMissingReturnStatement
|
||||
android {
|
||||
compileSdkVersion 25
|
||||
buildToolsVersion "25.0.2"
|
||||
compileSdkVersion 27
|
||||
buildToolsVersion "26.0.2"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "ch.dissem.apps." + appName.toLowerCase()
|
||||
applicationId "ch.dissem.apps.${appName.toLowerCase()}"
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 25
|
||||
versionCode 12
|
||||
versionName "1.0-beta12"
|
||||
jackOptions.enabled = false
|
||||
targetSdkVersion 27
|
||||
versionCode 18
|
||||
versionName "1.0-beta18"
|
||||
multiDexEnabled true
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_7
|
||||
targetCompatibility JavaVersion.VERSION_1_7
|
||||
}
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
@ -35,60 +39,77 @@ android {
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
}
|
||||
packagingOptions {
|
||||
exclude 'META-INF/core.kotlin_module'
|
||||
}
|
||||
testOptions {
|
||||
unitTests {
|
||||
includeAndroidResources = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//ext.jabitVersion = '2.0.4'
|
||||
ext.jabitVersion = 'feature-extended-encoding-SNAPSHOT'
|
||||
ext.supportVersion = '25.3.1'
|
||||
ext.jabitVersion = 'feature-refactoring-SNAPSHOT'
|
||||
ext.supportVersion = '27.0.2'
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
|
||||
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||
implementation "org.jetbrains.anko:anko:$anko_version"
|
||||
|
||||
compile "com.android.support:appcompat-v7:$supportVersion"
|
||||
compile "com.android.support:support-v4:$supportVersion"
|
||||
compile "com.android.support:design:$supportVersion"
|
||||
compile "com.android.support:multidex:1.0.1"
|
||||
implementation "com.android.support:appcompat-v7:$supportVersion"
|
||||
implementation "com.android.support:preference-v7:$supportVersion"
|
||||
implementation "com.android.support:cardview-v7:$supportVersion"
|
||||
implementation "com.android.support:support-v4:$supportVersion"
|
||||
implementation "com.android.support:design:$supportVersion"
|
||||
implementation "com.android.support:multidex:1.0.2"
|
||||
|
||||
compile "ch.dissem.jabit:jabit-core:$jabitVersion"
|
||||
compile "ch.dissem.jabit:jabit-networking:$jabitVersion"
|
||||
compile "ch.dissem.jabit:jabit-cryptography-spongy:$jabitVersion"
|
||||
compile "ch.dissem.jabit:jabit-extensions:$jabitVersion"
|
||||
compile "ch.dissem.jabit:jabit-wif:$jabitVersion"
|
||||
implementation "ch.dissem.jabit:jabit-core:$jabitVersion"
|
||||
implementation "ch.dissem.jabit:jabit-networking:$jabitVersion"
|
||||
implementation "ch.dissem.jabit:jabit-cryptography-spongy:$jabitVersion"
|
||||
implementation "ch.dissem.jabit:jabit-extensions:$jabitVersion"
|
||||
implementation "ch.dissem.jabit:jabit-wif:$jabitVersion"
|
||||
implementation "ch.dissem.jabit:jabit-exports:$jabitVersion"
|
||||
|
||||
compile 'org.slf4j:slf4j-android:1.7.25'
|
||||
implementation 'org.slf4j:slf4j-android:1.7.25'
|
||||
|
||||
compile 'com.mikepenz:materialize:1.0.1@aar'
|
||||
compile('com.mikepenz:materialdrawer:5.9.0@aar') {
|
||||
implementation 'com.mikepenz:materialize:1.1.2@aar'
|
||||
implementation('com.mikepenz:materialdrawer:6.0.2@aar') {
|
||||
transitive = true
|
||||
}
|
||||
compile('com.mikepenz:aboutlibraries:5.9.5@aar') {
|
||||
implementation('com.mikepenz:aboutlibraries:6.0.2@aar') {
|
||||
transitive = true
|
||||
}
|
||||
compile "com.mikepenz:iconics-core:2.8.3@aar"
|
||||
compile 'com.mikepenz:google-material-typeface:3.0.1.0.original@aar'
|
||||
compile 'com.mikepenz:community-material-typeface:1.9.32.1@aar'
|
||||
implementation "com.mikepenz:iconics-core:3.0.0@aar"
|
||||
implementation "com.mikepenz:iconics-views:3.0.0@aar"
|
||||
implementation 'com.mikepenz:google-material-typeface:3.0.1.2.original@aar'
|
||||
implementation 'com.mikepenz:community-material-typeface:2.0.46.1@aar'
|
||||
|
||||
compile 'com.journeyapps:zxing-android-embedded:3.5.0@aar'
|
||||
compile 'com.google.zxing:core:3.3.0'
|
||||
implementation 'com.journeyapps:zxing-android-embedded:3.5.0@aar'
|
||||
implementation 'com.google.zxing:core:3.3.1'
|
||||
|
||||
compile 'io.github.yavski:fab-speed-dial:1.0.6'
|
||||
compile 'com.github.amlcurran.showcaseview:library:5.4.3'
|
||||
compile('com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:0.10.4@aar') {
|
||||
implementation 'com.github.kobakei:MaterialFabSpeedDial:1.1.8'
|
||||
implementation 'com.github.amlcurran.showcaseview:library:5.4.3'
|
||||
implementation('com.github.h6ah4i:android-advancedrecyclerview:0.11.0@aar') {
|
||||
transitive = true
|
||||
}
|
||||
compile 'com.github.angads25:filepicker:1.1.0'
|
||||
compile 'com.android.support.constraint:constraint-layout:1.0.2'
|
||||
implementation 'com.github.angads25:filepicker:1.1.1'
|
||||
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
|
||||
|
||||
testCompile 'junit:junit:4.12'
|
||||
testCompile 'org.mockito:mockito-core:2.7.22'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
testImplementation 'org.mockito:mockito-core:2.13.0'
|
||||
testImplementation 'org.hamcrest:hamcrest-library:1.3'
|
||||
testImplementation 'com.nhaarman:mockito-kotlin-kt1.1:1.5.0'
|
||||
testImplementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||
testImplementation 'org.robolectric:robolectric:3.6.1'
|
||||
testImplementation "org.robolectric:shadows-multidex:3.6.1"
|
||||
|
||||
androidTestImplementation "com.android.support:multidex:1.0.2"
|
||||
}
|
||||
|
||||
idea.module {
|
||||
downloadJavadoc = true
|
||||
downloadSources = true
|
||||
}
|
||||
|
||||
android {
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
}
|
||||
|
@ -4,9 +4,9 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
|
||||
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
|
||||
@ -19,8 +19,7 @@
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme"
|
||||
android:name="android.support.multidex.MultiDexApplication"
|
||||
tools:replace="android:allowBackup">
|
||||
android:name="android.support.multidex.MultiDexApplication">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:label="@string/app_name">
|
||||
@ -84,16 +83,6 @@
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".SettingsActivity"
|
||||
android:label="@string/settings"
|
||||
android:parentActivityName=".MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MANAGE_NETWORK_USAGE"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".CreateAddressActivity"
|
||||
android:label="@string/title_activity_open_bitmessage_link"
|
||||
@ -144,6 +133,17 @@
|
||||
android:exported="false"
|
||||
android:syncable="true"/>
|
||||
|
||||
<!-- Exports -->
|
||||
<provider
|
||||
android:name="android.support.v4.content.FileProvider"
|
||||
android:authorities="ch.dissem.apps.abit.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_paths" />
|
||||
</provider>
|
||||
|
||||
<service
|
||||
android:name=".synchronization.AuthenticatorService"
|
||||
android:exported="true"
|
||||
@ -173,19 +173,28 @@
|
||||
android:exported="false"/>
|
||||
|
||||
<!-- Receive Wi-Fi connection state changes -->
|
||||
<receiver android:name=".listener.WifiReceiver">
|
||||
<receiver android:name=".listener.WifiReceiver" android:enabled="@bool/is_pre_api_21">
|
||||
<intent-filter>
|
||||
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver android:name=".service.StartServiceReceiver" android:enabled="@bool/is_post_api_21">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<service
|
||||
android:name=".service.StartupNodeOnWifiService"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
||||
android:exported="true"/>
|
||||
|
||||
<activity
|
||||
android:name=".StatusActivity"
|
||||
android:label="@string/title_activity_status"
|
||||
android:parentActivityName=".SettingsActivity">
|
||||
android:parentActivityName=".MainActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".SettingsActivity"/>
|
||||
android:value=".MainActivity"/>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
|
@ -1,146 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 Christian Basler
|
||||
*
|
||||
* 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 ch.dissem.apps.abit;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.ListFragment;
|
||||
import android.view.View;
|
||||
import android.widget.ListView;
|
||||
|
||||
import ch.dissem.apps.abit.listener.ListSelectionListener;
|
||||
|
||||
/**
|
||||
* @author Christian Basler
|
||||
*/
|
||||
public abstract class AbstractItemListFragment<T> extends ListFragment implements ListHolder {
|
||||
/**
|
||||
* The serialization (saved instance state) Bundle key representing the
|
||||
* activated item position. Only used on tablets.
|
||||
*/
|
||||
private static final String STATE_ACTIVATED_POSITION = "activated_position";
|
||||
/**
|
||||
* A dummy implementation of the {@link ListSelectionListener} interface that does
|
||||
* nothing. Used only when this fragment is not attached to an activity.
|
||||
*/
|
||||
private static final ListSelectionListener<Object> dummyCallbacks =
|
||||
new ListSelectionListener<Object>() {
|
||||
@Override
|
||||
public void onItemSelected(Object item) {
|
||||
// NO OP
|
||||
}
|
||||
};
|
||||
/**
|
||||
* The fragment's current callback object, which is notified of list item
|
||||
* clicks.
|
||||
*/
|
||||
private ListSelectionListener<? super T> callbacks = dummyCallbacks;
|
||||
/**
|
||||
* The current activated item position. Only used on tablets.
|
||||
*/
|
||||
private int activatedPosition = ListView.INVALID_POSITION;
|
||||
private boolean activateOnItemClick;
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
// Restore the previously serialized activated item position.
|
||||
if (savedInstanceState != null
|
||||
&& savedInstanceState.containsKey(STATE_ACTIVATED_POSITION)) {
|
||||
setActivatedPosition(savedInstanceState.getInt(STATE_ACTIVATED_POSITION));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
// When setting CHOICE_MODE_SINGLE, ListView will automatically
|
||||
// give items the 'activated' state when touched.
|
||||
getListView().setChoiceMode(activateOnItemClick
|
||||
? ListView.CHOICE_MODE_SINGLE
|
||||
: ListView.CHOICE_MODE_NONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
|
||||
// Activities containing this fragment must implement its callbacks.
|
||||
if (context instanceof ListSelectionListener) {
|
||||
//noinspection unchecked
|
||||
callbacks = (ListSelectionListener) context;
|
||||
} else {
|
||||
throw new IllegalStateException("Activity must implement fragment's callbacks.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetach() {
|
||||
super.onDetach();
|
||||
|
||||
// Reset the active callbacks interface to the dummy implementation.
|
||||
callbacks = dummyCallbacks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onListItemClick(ListView listView, View view, int position, long id) {
|
||||
super.onListItemClick(listView, view, position, id);
|
||||
|
||||
// Notify the active callbacks interface (the activity, if the
|
||||
// fragment is attached to one) that an item has been selected.
|
||||
//noinspection unchecked
|
||||
callbacks.onItemSelected((T) listView.getItemAtPosition(position));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
if (activatedPosition != ListView.INVALID_POSITION) {
|
||||
// Serialize and persist the activated item position.
|
||||
outState.putInt(STATE_ACTIVATED_POSITION, activatedPosition);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns on activate-on-click mode. When this mode is on, list items will be
|
||||
* given the 'activated' state when touched.
|
||||
*/
|
||||
public void setActivateOnItemClick(boolean activateOnItemClick) {
|
||||
this.activateOnItemClick = activateOnItemClick;
|
||||
|
||||
if (isVisible()) {
|
||||
// When setting CHOICE_MODE_SINGLE, ListView will automatically
|
||||
// give items the 'activated' state when touched.
|
||||
getListView().setChoiceMode(activateOnItemClick
|
||||
? ListView.CHOICE_MODE_SINGLE
|
||||
: ListView.CHOICE_MODE_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void setActivatedPosition(int position) {
|
||||
if (position == ListView.INVALID_POSITION) {
|
||||
getListView().setItemChecked(activatedPosition, false);
|
||||
} else {
|
||||
getListView().setItemChecked(position, true);
|
||||
}
|
||||
|
||||
activatedPosition = position;
|
||||
}
|
||||
}
|
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright 2015 Christian Basler
|
||||
*
|
||||
* 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 ch.dissem.apps.abit
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.support.v4.app.ListFragment
|
||||
import android.view.View
|
||||
import android.widget.ListView
|
||||
|
||||
import ch.dissem.apps.abit.listener.ListSelectionListener
|
||||
|
||||
/**
|
||||
* @author Christian Basler
|
||||
*/
|
||||
abstract class AbstractItemListFragment<L, T> : ListFragment(), ListHolder<L> {
|
||||
/**
|
||||
* The fragment's current callback object, which is notified of list item
|
||||
* clicks.
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private var callbacks: ListSelectionListener<T> = DummyCallback as ListSelectionListener<T>
|
||||
/**
|
||||
* The current activated item position. Only used on tablets.
|
||||
*/
|
||||
private var activatedPosition = ListView.INVALID_POSITION
|
||||
private var activateOnItemClick: Boolean = false
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
// Restore the previously serialized activated item position.
|
||||
if (savedInstanceState != null && savedInstanceState.containsKey(STATE_ACTIVATED_POSITION)) {
|
||||
setActivatedPosition(savedInstanceState.getInt(STATE_ACTIVATED_POSITION))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
// When setting CHOICE_MODE_SINGLE, ListView will automatically
|
||||
// give items the 'activated' state when touched.
|
||||
listView.choiceMode = if (activateOnItemClick)
|
||||
ListView.CHOICE_MODE_SINGLE
|
||||
else
|
||||
ListView.CHOICE_MODE_NONE
|
||||
}
|
||||
|
||||
override fun onAttach(context: Context?) {
|
||||
super.onAttach(context)
|
||||
|
||||
// Activities containing this fragment must implement its callbacks.
|
||||
if (context is ListSelectionListener<*>) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
callbacks = context as ListSelectionListener<T>
|
||||
} else {
|
||||
throw IllegalStateException("Activity must implement fragment's callbacks.")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onDetach() {
|
||||
super.onDetach()
|
||||
|
||||
// Reset the active callbacks interface to the dummy implementation.
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
callbacks = DummyCallback as ListSelectionListener<T>
|
||||
}
|
||||
|
||||
override fun onListItemClick(listView: ListView, view: View?, position: Int, id: Long) {
|
||||
super.onListItemClick(listView, view, position, id)
|
||||
|
||||
// Notify the active callbacks interface (the activity, if the
|
||||
// fragment is attached to one) that an item has been selected.
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
(listView.getItemAtPosition(position) as? T)?.let {
|
||||
callbacks.onItemSelected(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
if (activatedPosition != ListView.INVALID_POSITION) {
|
||||
// Serialize and persist the activated item position.
|
||||
outState.putInt(STATE_ACTIVATED_POSITION, activatedPosition)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns on activate-on-click mode. When this mode is on, list items will be
|
||||
* given the 'activated' state when touched.
|
||||
*/
|
||||
override fun setActivateOnItemClick(activateOnItemClick: Boolean) {
|
||||
this.activateOnItemClick = activateOnItemClick
|
||||
|
||||
if (isVisible) {
|
||||
// When setting CHOICE_MODE_SINGLE, ListView will automatically
|
||||
// give items the 'activated' state when touched.
|
||||
listView.choiceMode = if (activateOnItemClick)
|
||||
ListView.CHOICE_MODE_SINGLE
|
||||
else
|
||||
ListView.CHOICE_MODE_NONE
|
||||
}
|
||||
}
|
||||
|
||||
private fun setActivatedPosition(position: Int) {
|
||||
if (position == ListView.INVALID_POSITION) {
|
||||
listView.setItemChecked(activatedPosition, false)
|
||||
} else {
|
||||
listView.setItemChecked(position, true)
|
||||
}
|
||||
|
||||
activatedPosition = position
|
||||
}
|
||||
|
||||
override fun showPreviousList() = false
|
||||
|
||||
/**
|
||||
* A dummy implementation of the [ListSelectionListener] interface that does
|
||||
* nothing. Used only when this fragment is not attached to an activity.
|
||||
*/
|
||||
internal object DummyCallback : ListSelectionListener<Any> {
|
||||
override fun onItemSelected(item: Any) = Unit // NO OP
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* The serialization (saved instance state) Bundle key representing the
|
||||
* activated item position. Only used on tablets.
|
||||
*/
|
||||
internal const val STATE_ACTIVATED_POSITION = "activated_position"
|
||||
}
|
||||
}
|
@ -14,25 +14,25 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package ch.dissem.apps.abit;
|
||||
package ch.dissem.apps.abit
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.Bundle
|
||||
|
||||
|
||||
/**
|
||||
* An activity representing a single Subscription detail screen. This
|
||||
* activity is only used on handset devices. On tablet-size devices,
|
||||
* item details are presented side-by-side with a list of items
|
||||
* in a {@link MainActivity}.
|
||||
* <p/>
|
||||
* in a [MainActivity].
|
||||
*
|
||||
*
|
||||
* This activity is mostly just a 'shell' activity containing nothing
|
||||
* more than a {@link AddressDetailFragment}.
|
||||
* more than a [AddressDetailFragment].
|
||||
*/
|
||||
public class AddressDetailActivity extends DetailActivity {
|
||||
class AddressDetailActivity : DetailActivity() {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// savedInstanceState is non-null when there is fragment state
|
||||
// saved from previous configurations of this activity
|
||||
@ -42,18 +42,18 @@ public class AddressDetailActivity extends DetailActivity {
|
||||
// For more information, see the Fragments API guide at:
|
||||
//
|
||||
// http://developer.android.com/guide/components/fragments.html
|
||||
//
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
// Create the detail fragment and add it to the activity
|
||||
// using a fragment transaction.
|
||||
Bundle arguments = new Bundle();
|
||||
val arguments = Bundle()
|
||||
arguments.putSerializable(AddressDetailFragment.ARG_ITEM,
|
||||
getIntent().getSerializableExtra(AddressDetailFragment.ARG_ITEM));
|
||||
AddressDetailFragment fragment = new AddressDetailFragment();
|
||||
fragment.setArguments(arguments);
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
intent.getSerializableExtra(AddressDetailFragment.ARG_ITEM))
|
||||
val fragment = AddressDetailFragment()
|
||||
fragment.arguments = arguments
|
||||
supportFragmentManager.beginTransaction()
|
||||
.add(R.id.content, fragment)
|
||||
.commit();
|
||||
.commit()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,263 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 Christian Basler
|
||||
*
|
||||
* 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 ch.dissem.apps.abit;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
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.CompoundButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Switch;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.mikepenz.community_material_typeface_library.CommunityMaterial;
|
||||
import com.mikepenz.google_material_typeface_library.GoogleMaterial;
|
||||
|
||||
import ch.dissem.apps.abit.service.Singleton;
|
||||
import ch.dissem.apps.abit.util.Drawables;
|
||||
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||
import ch.dissem.bitmessage.wif.WifExporter;
|
||||
|
||||
|
||||
/**
|
||||
* A fragment representing a single Message detail screen.
|
||||
* This fragment is either contained in a {@link MainActivity}
|
||||
* in two-pane mode (on tablets) or a {@link MessageDetailActivity}
|
||||
* on handsets.
|
||||
*/
|
||||
public class AddressDetailFragment extends Fragment {
|
||||
/**
|
||||
* The fragment argument representing the item ID that this fragment
|
||||
* represents.
|
||||
*/
|
||||
public static final String ARG_ITEM = "item";
|
||||
public static final String EXPORT_POSTFIX = ".keys.dat";
|
||||
|
||||
/**
|
||||
* The content this fragment is presenting.
|
||||
*/
|
||||
private BitmessageAddress item;
|
||||
|
||||
|
||||
/**
|
||||
* Mandatory empty constructor for the fragment manager to instantiate the
|
||||
* fragment (e.g. upon screen orientation changes).
|
||||
*/
|
||||
public AddressDetailFragment() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (getArguments().containsKey(ARG_ITEM)) {
|
||||
// Load the dummy content specified by the fragment
|
||||
// arguments. In a real-world scenario, use a Loader
|
||||
// to load content from a content provider.
|
||||
item = (BitmessageAddress) getArguments().getSerializable(ARG_ITEM);
|
||||
}
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.address, menu);
|
||||
|
||||
FragmentActivity activity = getActivity();
|
||||
Drawables.addIcon(activity, menu, R.id.write_message, GoogleMaterial.Icon.gmd_mail);
|
||||
Drawables.addIcon(activity, menu, R.id.share, GoogleMaterial.Icon.gmd_share);
|
||||
Drawables.addIcon(activity, menu, R.id.delete, GoogleMaterial.Icon.gmd_delete);
|
||||
Drawables.addIcon(activity, menu, R.id.export,
|
||||
CommunityMaterial.Icon.cmd_export)
|
||||
.setVisible(item != null && item.getPrivateKey() != null);
|
||||
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem menuItem) {
|
||||
final Activity ctx = getActivity();
|
||||
switch (menuItem.getItemId()) {
|
||||
case R.id.write_message: {
|
||||
BitmessageAddress identity = Singleton.getIdentity(ctx);
|
||||
if (identity == null) {
|
||||
Toast.makeText(ctx, R.string.no_identity_warning, Toast.LENGTH_LONG).show();
|
||||
} else {
|
||||
Intent intent = new Intent(ctx, ComposeMessageActivity.class);
|
||||
intent.putExtra(ComposeMessageActivity.EXTRA_IDENTITY, identity);
|
||||
intent.putExtra(ComposeMessageActivity.EXTRA_RECIPIENT, item);
|
||||
startActivity(intent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case R.id.delete: {
|
||||
int warning;
|
||||
if (item.getPrivateKey() != null)
|
||||
warning = R.string.delete_identity_warning;
|
||||
else
|
||||
warning = R.string.delete_contact_warning;
|
||||
new AlertDialog.Builder(ctx)
|
||||
.setMessage(warning)
|
||||
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
Singleton.getAddressRepository(ctx).remove(item);
|
||||
MainActivity mainActivity = MainActivity.getInstance();
|
||||
if (item.getPrivateKey() != null && mainActivity != null) {
|
||||
mainActivity.removeIdentityEntry(item);
|
||||
}
|
||||
item = null;
|
||||
ctx.onBackPressed();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(android.R.string.no, null)
|
||||
.show();
|
||||
return true;
|
||||
}
|
||||
case R.id.export: {
|
||||
new AlertDialog.Builder(ctx)
|
||||
.setMessage(R.string.confirm_export)
|
||||
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
Intent shareIntent = new Intent(Intent.ACTION_SEND);
|
||||
shareIntent.setType("text/plain");
|
||||
shareIntent.putExtra(Intent.EXTRA_TITLE, item +
|
||||
EXPORT_POSTFIX);
|
||||
WifExporter exporter = new WifExporter(Singleton
|
||||
.getBitmessageContext(ctx));
|
||||
exporter.addIdentity(item);
|
||||
shareIntent.putExtra(Intent.EXTRA_TEXT, exporter.toString
|
||||
());
|
||||
startActivity(Intent.createChooser(shareIntent, null));
|
||||
}
|
||||
})
|
||||
.setNegativeButton(android.R.string.no, null)
|
||||
.show();
|
||||
return true;
|
||||
}
|
||||
case R.id.share: {
|
||||
Intent shareIntent = new Intent(Intent.ACTION_SEND);
|
||||
shareIntent.setType("text/plain");
|
||||
shareIntent.putExtra(Intent.EXTRA_TEXT, item.getAddress());
|
||||
startActivity(Intent.createChooser(shareIntent, null));
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View rootView = inflater.inflate(R.layout.fragment_address_detail, container, false);
|
||||
|
||||
// Show the dummy content as text in a TextView.
|
||||
if (item != null) {
|
||||
FragmentActivity activity = getActivity();
|
||||
if (item.isChan()) {
|
||||
activity.setTitle(R.string.title_chan_detail);
|
||||
} else if (item.getPrivateKey() != null) {
|
||||
activity.setTitle(R.string.title_identity_detail);
|
||||
} else if (item.isSubscribed()) {
|
||||
activity.setTitle(R.string.title_subscription_detail);
|
||||
} else {
|
||||
activity.setTitle(R.string.title_contact_detail);
|
||||
}
|
||||
|
||||
((ImageView) rootView.findViewById(R.id.avatar)).setImageDrawable(new Identicon(item));
|
||||
TextView name = (TextView) rootView.findViewById(R.id.name);
|
||||
name.setText(item.toString());
|
||||
name.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
item.setAlias(s.toString());
|
||||
}
|
||||
});
|
||||
TextView address = (TextView) rootView.findViewById(R.id.address);
|
||||
address.setText(item.getAddress());
|
||||
address.setSelected(true);
|
||||
((TextView) rootView.findViewById(R.id.stream_number)).setText(
|
||||
getString(R.string.stream_number, item.getStream()));
|
||||
if (item.getPrivateKey() == null) {
|
||||
Switch active = (Switch) rootView.findViewById(R.id.active);
|
||||
active.setChecked(item.isSubscribed());
|
||||
active.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton button, boolean checked) {
|
||||
item.setSubscribed(checked);
|
||||
}
|
||||
});
|
||||
|
||||
ImageView pubkeyAvailableImg = (ImageView) rootView.findViewById(R.id
|
||||
.pubkey_available);
|
||||
|
||||
if (item.getPubkey() == null) {
|
||||
pubkeyAvailableImg.setAlpha(0.3f);
|
||||
TextView pubkeyAvailableDesc = (TextView) rootView.findViewById(R.id
|
||||
.pubkey_available_desc);
|
||||
pubkeyAvailableDesc.setText(R.string.pubkey_not_available);
|
||||
}
|
||||
} else {
|
||||
rootView.findViewById(R.id.active).setVisibility(View.GONE);
|
||||
rootView.findViewById(R.id.pubkey_available).setVisibility(View.GONE);
|
||||
rootView.findViewById(R.id.pubkey_available_desc).setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// QR code
|
||||
ImageView qrCode = (ImageView) rootView.findViewById(R.id.qr_code);
|
||||
qrCode.setImageBitmap(Drawables.qrCode(item));
|
||||
}
|
||||
|
||||
return rootView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
if (item != null) {
|
||||
Singleton.getAddressRepository(getContext()).save(item);
|
||||
MainActivity mainActivity = MainActivity.getInstance();
|
||||
if (mainActivity != null && item.getPrivateKey() != null) {
|
||||
mainActivity.updateIdentityEntry(item);
|
||||
}
|
||||
}
|
||||
super.onPause();
|
||||
}
|
||||
}
|
210
app/src/main/java/ch/dissem/apps/abit/AddressDetailFragment.kt
Normal file
210
app/src/main/java/ch/dissem/apps/abit/AddressDetailFragment.kt
Normal file
@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright 2015 Christian Basler
|
||||
*
|
||||
* 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 ch.dissem.apps.abit
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.support.v4.app.Fragment
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.*
|
||||
import android.widget.Toast
|
||||
import ch.dissem.apps.abit.service.Singleton
|
||||
import ch.dissem.apps.abit.util.Drawables
|
||||
import ch.dissem.bitmessage.entity.BitmessageAddress
|
||||
import ch.dissem.bitmessage.wif.WifExporter
|
||||
import com.mikepenz.community_material_typeface_library.CommunityMaterial
|
||||
import com.mikepenz.google_material_typeface_library.GoogleMaterial
|
||||
import kotlinx.android.synthetic.main.fragment_address_detail.*
|
||||
|
||||
|
||||
/**
|
||||
* A fragment representing a single Message detail screen.
|
||||
* This fragment is either contained in a [MainActivity]
|
||||
* in two-pane mode (on tablets) or a [MessageDetailActivity]
|
||||
* on handsets.
|
||||
*/
|
||||
class AddressDetailFragment : Fragment() {
|
||||
|
||||
/**
|
||||
* The content this fragment is presenting.
|
||||
*/
|
||||
private var item: BitmessageAddress? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
arguments?.let { arguments ->
|
||||
if (arguments.containsKey(ARG_ITEM)) {
|
||||
item = arguments.getSerializable(ARG_ITEM) as BitmessageAddress
|
||||
}
|
||||
}
|
||||
setHasOptionsMenu(true)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.address, menu)
|
||||
|
||||
val ctx = activity!!
|
||||
Drawables.addIcon(ctx, menu, R.id.write_message, GoogleMaterial.Icon.gmd_mail)
|
||||
Drawables.addIcon(ctx, menu, R.id.share, GoogleMaterial.Icon.gmd_share)
|
||||
Drawables.addIcon(ctx, menu, R.id.delete, GoogleMaterial.Icon.gmd_delete)
|
||||
Drawables.addIcon(ctx, menu, R.id.export, CommunityMaterial.Icon.cmd_export).isVisible = item?.privateKey != null
|
||||
|
||||
super.onCreateOptionsMenu(menu, inflater)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(menuItem: MenuItem): Boolean {
|
||||
val item = item ?: return false
|
||||
val ctx = activity ?: return false
|
||||
when (menuItem.itemId) {
|
||||
R.id.write_message -> {
|
||||
val identity = Singleton.getIdentity(ctx)
|
||||
if (identity == null) {
|
||||
Toast.makeText(ctx, R.string.no_identity_warning, Toast.LENGTH_LONG).show()
|
||||
} else {
|
||||
val intent = Intent(ctx, ComposeMessageActivity::class.java)
|
||||
intent.putExtra(ComposeMessageActivity.EXTRA_IDENTITY, identity)
|
||||
intent.putExtra(ComposeMessageActivity.EXTRA_RECIPIENT, item)
|
||||
startActivity(intent)
|
||||
}
|
||||
return true
|
||||
}
|
||||
R.id.delete -> {
|
||||
val warning = if (item.privateKey != null)
|
||||
R.string.delete_identity_warning
|
||||
else
|
||||
R.string.delete_contact_warning
|
||||
AlertDialog.Builder(ctx)
|
||||
.setMessage(warning)
|
||||
.setPositiveButton(android.R.string.yes) { _, _ ->
|
||||
Singleton.getAddressRepository(ctx).remove(item)
|
||||
MainActivity.apply {
|
||||
if (item.privateKey != null) {
|
||||
removeIdentityEntry(item)
|
||||
}
|
||||
}
|
||||
this.item = null
|
||||
ctx.onBackPressed()
|
||||
}
|
||||
.setNegativeButton(android.R.string.no, null)
|
||||
.show()
|
||||
return true
|
||||
}
|
||||
R.id.export -> {
|
||||
AlertDialog.Builder(ctx)
|
||||
.setMessage(R.string.confirm_export)
|
||||
.setPositiveButton(android.R.string.yes) { _, _ ->
|
||||
val shareIntent = Intent(Intent.ACTION_SEND).apply {
|
||||
type = "text/plain"
|
||||
putExtra(
|
||||
Intent.EXTRA_TITLE,
|
||||
"$item$EXPORT_POSTFIX"
|
||||
)
|
||||
putExtra(
|
||||
Intent.EXTRA_TEXT,
|
||||
WifExporter(Singleton.getBitmessageContext(ctx)).apply {
|
||||
addIdentity(item)
|
||||
}.toString()
|
||||
)
|
||||
}
|
||||
startActivity(Intent.createChooser(shareIntent, null))
|
||||
}
|
||||
.setNegativeButton(android.R.string.no, null)
|
||||
.show()
|
||||
return true
|
||||
}
|
||||
R.id.share -> {
|
||||
val shareIntent = Intent(Intent.ACTION_SEND)
|
||||
shareIntent.type = "text/plain"
|
||||
shareIntent.putExtra(Intent.EXTRA_TEXT, item.address)
|
||||
startActivity(Intent.createChooser(shareIntent, null))
|
||||
return true
|
||||
}
|
||||
else -> return false
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View
|
||||
= inflater.inflate(R.layout.fragment_address_detail, container, false)
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
// Show the dummy content as text in a TextView.
|
||||
item?.let { item ->
|
||||
activity?.let { activity ->
|
||||
when {
|
||||
item.isChan -> activity.setTitle(R.string.title_chan_detail)
|
||||
item.privateKey != null -> activity.setTitle(R.string.title_identity_detail)
|
||||
item.isSubscribed -> activity.setTitle(R.string.title_subscription_detail)
|
||||
else -> activity.setTitle(R.string.title_contact_detail)
|
||||
}
|
||||
}
|
||||
|
||||
avatar.setImageDrawable(Identicon(item))
|
||||
name.setText(item.toString())
|
||||
name.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) = Unit // Nothing to do
|
||||
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) = Unit // Nothing to do
|
||||
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
item.alias = s.toString()
|
||||
}
|
||||
})
|
||||
address.text = item.address
|
||||
address.isSelected = true
|
||||
stream_number.text = getString(R.string.stream_number, item.stream)
|
||||
if (item.privateKey == null) {
|
||||
active.isChecked = item.isSubscribed
|
||||
active.setOnCheckedChangeListener { _, checked -> item.isSubscribed = checked }
|
||||
|
||||
if (item.pubkey == null) {
|
||||
pubkey_available.alpha = 0.3f
|
||||
pubkey_available_desc.setText(R.string.pubkey_not_available)
|
||||
}
|
||||
} else {
|
||||
active.visibility = View.GONE
|
||||
pubkey_available.visibility = View.GONE
|
||||
pubkey_available_desc.visibility = View.GONE
|
||||
}
|
||||
|
||||
// QR code
|
||||
qr_code.setImageBitmap(Drawables.qrCode(item))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
item?.let { item ->
|
||||
Singleton.getAddressRepository(context!!).save(item)
|
||||
if (item.privateKey != null) {
|
||||
MainActivity.apply { updateIdentityEntry(item) }
|
||||
}
|
||||
}
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* The fragment argument representing the item ID that this fragment
|
||||
* represents.
|
||||
*/
|
||||
val ARG_ITEM = "item"
|
||||
val EXPORT_POSTFIX = ".keys.dat"
|
||||
}
|
||||
}
|
@ -1,166 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 Christian Basler
|
||||
*
|
||||
* 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 ch.dissem.apps.abit;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.zxing.integration.android.IntentIntegrator;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import ch.dissem.apps.abit.listener.ActionBarListener;
|
||||
import ch.dissem.apps.abit.service.Singleton;
|
||||
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||
import ch.dissem.bitmessage.entity.valueobject.Label;
|
||||
import io.github.yavski.fabspeeddial.FabSpeedDial;
|
||||
import io.github.yavski.fabspeeddial.SimpleMenuListenerAdapter;
|
||||
|
||||
/**
|
||||
* Fragment that shows a list of all contacts, the ones we subscribed to first.
|
||||
*/
|
||||
public class AddressListFragment extends AbstractItemListFragment<BitmessageAddress> {
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
updateList();
|
||||
}
|
||||
|
||||
public void updateList() {
|
||||
List<BitmessageAddress> addresses = Singleton.getAddressRepository(getContext())
|
||||
.getContacts();
|
||||
Collections.sort(addresses, new Comparator<BitmessageAddress>() {
|
||||
@Override
|
||||
public int compare(BitmessageAddress lhs, BitmessageAddress rhs) {
|
||||
// Yields the following order:
|
||||
// * Subscribed addresses come first
|
||||
// * Addresses with Aliases (alphabetically)
|
||||
// * Addresses (alphabetically)
|
||||
if (lhs.isSubscribed() == rhs.isSubscribed()) {
|
||||
if (lhs.getAlias() != null) {
|
||||
if (rhs.getAlias() != null) {
|
||||
return lhs.getAlias().compareTo(rhs.getAlias());
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else if (rhs.getAlias() != null) {
|
||||
return 1;
|
||||
} else {
|
||||
return lhs.getAddress().compareTo(rhs.getAddress());
|
||||
}
|
||||
}
|
||||
if (lhs.isSubscribed()) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
setListAdapter(new ArrayAdapter<BitmessageAddress>(
|
||||
getActivity(),
|
||||
android.R.layout.simple_list_item_activated_1,
|
||||
android.R.id.text1,
|
||||
addresses) {
|
||||
@NonNull
|
||||
@Override
|
||||
public View getView(int position, View convertView, @NonNull ViewGroup parent) {
|
||||
if (convertView == null) {
|
||||
LayoutInflater inflater = LayoutInflater.from(getContext());
|
||||
convertView = inflater.inflate(R.layout.subscription_row, parent, false);
|
||||
}
|
||||
BitmessageAddress item = getItem(position);
|
||||
assert item != null;
|
||||
((ImageView) convertView.findViewById(R.id.avatar)).setImageDrawable(new
|
||||
Identicon(item));
|
||||
TextView name = (TextView) convertView.findViewById(R.id.name);
|
||||
name.setText(item.toString());
|
||||
TextView streamNumber = (TextView) convertView.findViewById(R.id.stream_number);
|
||||
streamNumber.setText(getContext().getString(R.string.stream_number,
|
||||
item.getStream()));
|
||||
convertView.findViewById(R.id.subscribed).setVisibility(item.isSubscribed() ?
|
||||
View.VISIBLE : View.INVISIBLE);
|
||||
return convertView;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context ctx) {
|
||||
super.onAttach(ctx);
|
||||
if (ctx instanceof ActionBarListener) {
|
||||
((ActionBarListener) ctx).updateTitle(getString(R.string.contacts_and_subscriptions));
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle
|
||||
savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_address_list, container, false);
|
||||
|
||||
FabSpeedDial fabSpeedDial = (FabSpeedDial) view.findViewById(R.id.fab_add_contact);
|
||||
fabSpeedDial.setMenuListener(new SimpleMenuListenerAdapter() {
|
||||
@Override
|
||||
public boolean onMenuItemSelected(MenuItem menuItem) {
|
||||
switch (menuItem.getItemId()) {
|
||||
case R.id.action_read_qr_code:
|
||||
IntentIntegrator.forSupportFragment(AddressListFragment.this)
|
||||
.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE_TYPES)
|
||||
.initiateScan();
|
||||
return true;
|
||||
case R.id.action_create_contact:
|
||||
Intent intent = new Intent(getActivity(), CreateAddressActivity.class);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (data != null && data.hasExtra("SCAN_RESULT")) {
|
||||
Uri uri = Uri.parse(data.getStringExtra("SCAN_RESULT"));
|
||||
Intent intent = new Intent(getActivity(), CreateAddressActivity.class);
|
||||
intent.setData(uri);
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateList(Label label) {
|
||||
updateList();
|
||||
}
|
||||
}
|
145
app/src/main/java/ch/dissem/apps/abit/AddressListFragment.kt
Normal file
145
app/src/main/java/ch/dissem/apps/abit/AddressListFragment.kt
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright 2015 Christian Basler
|
||||
*
|
||||
* 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 ch.dissem.apps.abit
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import ch.dissem.apps.abit.service.Singleton
|
||||
import ch.dissem.apps.abit.util.FabUtils
|
||||
import ch.dissem.bitmessage.entity.BitmessageAddress
|
||||
import com.google.zxing.integration.android.IntentIntegrator
|
||||
import io.github.kobakei.materialfabspeeddial.FabSpeedDialMenu
|
||||
import org.jetbrains.anko.doAsync
|
||||
import org.jetbrains.anko.uiThread
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Fragment that shows a list of all contacts, the ones we subscribed to first.
|
||||
*/
|
||||
class AddressListFragment : AbstractItemListFragment<Void, BitmessageAddress>() {
|
||||
private lateinit var adapter: ArrayAdapter<BitmessageAddress>
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
adapter = object : ArrayAdapter<BitmessageAddress>(
|
||||
activity,
|
||||
R.layout.subscription_row,
|
||||
R.id.name,
|
||||
LinkedList()) {
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
val result: View
|
||||
val v: ViewHolder
|
||||
if (convertView == null) {
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val view = inflater.inflate(R.layout.subscription_row, parent, false)
|
||||
v = ViewHolder(
|
||||
ctx = context,
|
||||
avatar = view.findViewById(R.id.avatar),
|
||||
name = view.findViewById(R.id.name),
|
||||
streamNumber = view.findViewById(R.id.stream_number),
|
||||
subscribed = view.findViewById(R.id.subscribed)
|
||||
)
|
||||
view.tag = v
|
||||
result = view
|
||||
} else {
|
||||
v = convertView.tag as ViewHolder
|
||||
result = convertView
|
||||
}
|
||||
getItem(position)?.let { item ->
|
||||
v.avatar.setImageDrawable(Identicon(item))
|
||||
v.name.text = item.toString()
|
||||
v.streamNumber.text = v.ctx.getString(R.string.stream_number, item.stream)
|
||||
v.subscribed.visibility = if (item.isSubscribed) View.VISIBLE else View.INVISIBLE
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
listAdapter = adapter
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
initFab(activity as MainActivity)
|
||||
updateList()
|
||||
}
|
||||
|
||||
fun updateList() {
|
||||
adapter.clear()
|
||||
context?.let { context ->
|
||||
val addressRepo = Singleton.getAddressRepository(context)
|
||||
doAsync {
|
||||
addressRepo.getContactIds()
|
||||
.map { addressRepo.getAddress(it) }
|
||||
.forEach { address -> uiThread { adapter.add(address) } }
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initFab(activity: MainActivity) {
|
||||
activity.updateTitle(getString(R.string.contacts_and_subscriptions))
|
||||
val menu = FabSpeedDialMenu(activity)
|
||||
menu.add(R.string.scan_qr_code).setIcon(R.drawable.ic_action_qr_code)
|
||||
menu.add(R.string.create_contact).setIcon(R.drawable.ic_action_create_contact)
|
||||
FabUtils.initFab(activity, R.drawable.ic_action_add_contact, menu)
|
||||
.addOnMenuItemClickListener { _, _, itemId ->
|
||||
when (itemId) {
|
||||
1 -> IntentIntegrator.forSupportFragment(this@AddressListFragment)
|
||||
.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE_TYPES)
|
||||
.initiateScan()
|
||||
2 -> {
|
||||
val intent = Intent(getActivity(), CreateAddressActivity::class.java)
|
||||
startActivity(intent)
|
||||
}
|
||||
else -> {
|
||||