Android 7.1 ( API25 / N_MR1 ) の通知の設定

今や購入時のままの設定のままだと各アプリからの通知が大量に溜まってくる.

Android 7.1 では, 端末側の設定画面が変わっている.

「設定」-「通知」-「右上の歯車マーク」から設定できるのは以下.

- 光を点滅させて通知 (ON/OFF)

- ロック画面の通知 (通知をすべて表示しない/すべての通知内容を表示する)

- Power notification controls (ON/OFF)

画面上部のスピナーからも設定状態別にアプリを抽出表示できる.

- すべてのアプリ
- ブロック中
- マナーモードで表示
- ロック画面にプライベートな内容を表示しない
- ロック画面に表示しない
- [通知を非表示]をオーバーライド

「ブロック中」を選択してみると, いつのまにか Evernote をブロックしていた.

「重要度」というスライダーがあるのだが, 7段階の設定ができる.

- このアプリからの通知を表示しない

- 全画面表示, ポップアップ, 音, バイブレーションを使用しない。通知リストの一番下に表示する。ロック画面やステータスバーには表示しない。

- 全画面表示, ポップアップ, 音, バイブレーションを使用しない。通知リストの一番下に表示する。ロック画面には表示しない。

- 全画面表示, ポップアップ, 音, バイブレーションを使用しない。

- 全画面やポップアップを使用しない。

- 常にポップアップし, 全画面は表示しない。

- 常にポップアップし, 全画面表示も許可する。通知リストの一番上に表示する。

あるいは, 重要度と書いてる下の「A」を押すと, スライダーは利用できなくなり以下となる

- アプリが通知ごとに重要度を識別する。

スライダーの下にはスイッチ.

- [通知を非表示]をオーバーライドする。(ON/OFF)

その下には,「通知音の最少間隔」の設定。

- 無制限
- 10秒
- 30秒
- 1分
...
- 30分

アプリによっては, このアプリごと画面の右上から, アプリ内の設定画面に遷移できる.

と, かなり設定項目多いです. 多すぎません?

まとめ

通知の内容や頻度はアプリを公開している側の考え方に依存していますので, まずは単純にアプリ別に「ブロックする」方法を覚えておくといいのだろうと思います.

「設定」-「通知」で通知してきたアプリを選択.

重要度の下の「A」をタップして, スライダーでこのアプリからの通知をブロック.

これまでの「フィルター」機能 消してしまった通知の確認 はもうできないのか・・・

しかし, 最近のスマホてややこしすぎません? いや, ほんと.

通知が表示されない とかどうせブロックの設定だろ! → 原因「フィルター」

消してしまった不要な通知を2度とこないように「通知履歴」から非表示設定する


RecyclerView のアイテムの中のある部分のクリック

list_mail

まずは, 入れ子なしでばらして書かせたほうがいいような気がするのです.

java - Why doesn't RecyclerView have onItemClickListener()? And how RecyclerView is different from Listview? - Stack Overflow


public static class MyViewHolder extends RecyclerView.ViewHolder {

  public TextView text;
  public ImageView image;
  public OnClickPartsListener listener;

  public MyViewHolder(View itemView, OnClickPartsListener listener) {
    super(itemView);

    this.listener = listener;
    text = (TextView) itemView.findViewById(R.id.text);
    image = (ImageView) itemView.findViewById(R.id.image);

    text.setOnClickListener(new View.OnClickListener {
      listener.onClickText("Text Clicked!!");
    });

    image.setOnClickListener(new View.OnClickListener {
      listener.onClickImage("Image Clicked!!");
    });

  }

  public interface OnClickPartsListener {
    void onClickText(String message);
    void onClickImage(String message);
  }

}


public class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {

  private MyViewHolder.OnClickPartsListener listener;

  public MyAdapter(MyViewHolder.OnClickPartsListener listener) {
    this.listener = listener;
  }

  @Override
  public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    //..	 
    return new MyViewHolder(view, listener);
  }

}


public class MyActivity extends AppCompatActivity {  
  
  @Override  
  protected void onCreate(Bundle savedInstanceState) {  

    MyViewHolder.OnClickPartsListener listener =
      new MyViewHolder.OnClickPartsListener() {
  
        public void onClickText(String message) {
          Toast.makeText(this, message, LENGTH_SHORT).show();  
        }

        public void onClickImage(String message) {
          Toast.makeText(this, message, LENGTH_SHORT).show();  
        }

      };

    recyclerView = (RecyclerView) findViewById(R.id.recycler_view);  
    adapter = new MyAdapter(listener) {  
    recyclerView.setAdapter(adapter);  

  }

}

めんどくさいので,


public class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {

  private final PublishSubject<String> onClickSubject = PublishSubject.create();

  @Override 
  public void onBindViewHolder(final ViewHolder holder, int position) {

    final Item item = items.get(position);

    holder.itemView.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        onClickSubject.onNext(item);
      }
    });
  }

  public Observable<String> getPositionClicks(){
    return onClickSubject.asObservable();
  }

}

または, イベントバス..

まとめ

親や祖父母側で実装した子や孫のインターフェースをコンストラクタ経由で渡し返す.

tag を上手に使うとよい.


よくある Observable の入れ子ができない頭の硬さ乙

99

なぜか長ったらしくなる Observable のネスト.


Observable.just("yo")
    .flatMap(s -> Observable.range(0, 100))
    .subscribe(integer -> Ln.d("Here's an Integer(%s), but how do I access that juicy String?", integer));

Passing composite data through flatMap and similar operators without creating container objects · Issue #2931 · ReactiveX/RxJava

よくあるくせに綺麗に書けない.

そんなわたし乙.

lambda も絡めてゆっくりと眺める.

元.


Observable.just("yo")
    .flatMap(new Func1<String, Observable<? extends Integer>>() {
      @Override public Observable<? extends Integer> call(String s) {
        return Observable.range(0, 100);
      }
    })
    .map(new Func1<Integer, String>() {
      @Override public String call(Integer integer) {
        return String.format("Here's an Integer(%s), but how do I access that juicy String?", integer);
      }
    })
    .subscribe(new Action1<String>() {
      @Override public void call(String x) {
        System.out.println(x);
      }
    });

flatMap(Func1, Func2) でやる場合.


Observable.just("yo")
    .flatMap(new Func1<String, Observable<? extends Integer>>() {
      @Override public Observable<? extends Integer> call(String s) {
        return Observable.range(0, 100);
      }
    }, new Func2<String, Integer, String>() {
      @Override public String call(String s, Integer integer) {
        return String.format("Here's an Integer(%s), with String(%s)", integer, s);
      }
    })
    .subscribe(new Action1<String>() {
      @Override public void call(String x) {
        System.out.println(x);
      }
    });

flatmap に map を入れ子でやる場合.


Observable.just("yo")
    .flatMap(new Func1<String, Observable<? extends String>>() {
      @Override public Observable<? extends String> call(String s) {
        return Observable.range(0, 100)
            .map(new Func1<Integer, String>() {
              @Override public String call(Integer integer) {
                return String.format("Here's an Integer(%s), with String(%s)", integer, s);
              }
            });
      }
    })
    .subscribe(new Action1<String>() {
      @Override public void call(String x) {
        System.out.println(x);
      }
    });
  }

lambda へ

元.


Observable.just("yo")
    .flatMap(s -> Observable.range(0, 100))
    .map(integer -> String.format("Here's an Integer(%s), but how do I access that juicy String?", integer))
    .subscribe(System.out::println);

flatMap(Func1, Func2) でやる場合.


Observable.just("yo")
    .flatMap(s -> Observable.range(0, 100),
        (s, integer) -> String.format("Here's an Integer(%s), with String(%s)", integer, s))
    .subscribe(System.out::println);

flatmap に map を入れ子でやる場合.


Observable.just("yo")
    .flatMap(s -> Observable.range(0, 100)
        .map(integer -> String.format("Here's an Integer(%s), with String(%s)", integer, s)))
    .subscribe(System.out::println);

まとめ

flatmap に map を入れるのがわかりやすいように思います.

慣れたらラムダも直感的です.

Feature request: @Passthru variable for Rx chains · Issue #855 · square/retrofit


Espresso で アイドリング

espresso

Espresso は以下の非同期処理は考慮してくれるようですが.

- メッセージキューを利用したUIイベント
- デフォルト AsyncTask のスレッドプールを利用したタスク

Square Island: Espresso: Custom Idling Resource

これらに該当しない処理でも, テスト実行を「あるタイミング」まで待たせたいときがありますよね?

カスタムした「IdlingResource」を使ってみましょう.

Espresso Idling Resource

ここで書かれているアプローチとしては2つ.

Counting running jobs: When a job starts, increment a counter. When it finishes, decrement it. The app is idle if the counter is zero. This approach is very simple and accounts for most situations. CountingIdlingResource does exactly this.

Querying state: It might be more reliable to ask a work queue or an HTTP client (or whatever is doing the background work) if it is busy. If the state is exposed, implementing an Idling Resource is trivial.

これらのうち, 汎用的に使えるジョブカウンターを利用した方法を, Googleサンプルを眺めつつ手順整理しておきます.

Android Testing Codelab

1. IdlingResource ユーティリティの作成

アイドリング向けのユーテリティを作成します.
そのまま2つのクラスを利用してもいいかもしれません.

EspressoIdlingResource.java at master · googlecodelabs/android-testing

SimpleCountingIdlingResource.java at master · googlecodelabs/android-testing

2. テスト対象の Activity に記述

テスト対象となる Activity にテスト時に利用するメソッドを記述しておきます.
テスト時にはこのメソッドを利用して, 先ほどのユーティリティを利用登録します.


    @VisibleForTesting
    public IdlingResource getCountingIdlingResource() {
        return EspressoIdlingResource.getIdlingResource();
    }

AddNoteActivity.java at master · googlecodelabs/android-testing

NotesActivity.java at master · googlecodelabs/android-testing

3. 「待たせる」処理の記述

Presenter や View などのテスト対象クラス内にアイドリングの利用タイミングを記述しておきます.


EspressoIdlingResource.increment(); 


EspressoIdlingResource.decrement(); 

カウンターを増減しながらアイドリングを操作します.
カウンターが0になると処理が再開されます.


    @Override
    public void loadNotes(boolean forceUpdate) {
        mNotesView.setProgressIndicator(true);
        if (forceUpdate) {
            mNotesRepository.refreshData();
        }

        // The network request might be handled in a different thread so make sure Espresso knows
        // that the app is busy until the response is handled.
        EspressoIdlingResource.increment(); // App is busy until further notice

        mNotesRepository.getNotes(new NotesRepository.LoadNotesCallback() {
            @Override
            public void onNotesLoaded(List<Note> notes) {
                EspressoIdlingResource.decrement(); // Set app as idle.
                mNotesView.setProgressIndicator(false);
                mNotesView.showNotes(notes);
            }
        });
    }

AddNoteFragment.java at master · googlecodelabs/android-testing

NotesPresenter.java at master · googlecodelabs/android-testing

4. テスト内で登録する

テスト対象Activityに記述したメソッドを利用してテストコードへ登録します
アイドリング処理がテスト時に反映されます.


    @Before
    public void registerIdlingResource() {
        Espresso.registerIdlingResources(
                mAddNoteIntentsTestRule.getActivity().getCountingIdlingResource());
    }

    @After
    public void unregisterIdlingResource() {
        Espresso.unregisterIdlingResources(
                mAddNoteIntentsTestRule.getActivity().getCountingIdlingResource());
    }

AddNoteScreenTest.java at master · googlecodelabs/android-testing

まとめ

テストにかかるコストがただの自己満足で終わらないようにしたいです.

JakeWharton/okhttp-idling-resource: An Espresso IdlingResource for OkHttp.

PSA: Dont Use Espresso Idling Resources like Google does · Philosophical Hacker


意味不明な Gradle の Warning や Error を消して激ビルド時間短縮

なんとなく Gradle コンソールを開けてみると死ぬほど書き出されてるエラーやワーニング.


AGPBI: {"kind":"error","text":"(org.apache.commons.beanutils.BeanMap$8) that doesn\u0027t come with an","sources":[{}]}
AGPBI: {"kind":"error","text":"associated EnclosingMethod attribute. This class was probably produced by a","sources":[{}]}
AGPBI: {"kind":"error","text":"compiler that did not target the modern .class file format. The recommended","sources":[{}]}
AGPBI: {"kind":"error","text":"solution is to recompile the class from source, using an up-to-date compiler","sources":[{}]}
AGPBI: {"kind":"error","text":"and without specifying any \"-target\" type options. The consequence of ignoring","sources":[{}]}
AGPBI: {"kind":"error","text":"this warning is that reflective operations on this class will incorrectly","sources":[{}]}
AGPBI: {"kind":"error","text":"indicate that it is *not* an inner class.","sources":[{}]}
AGPBI: {"kind":"error","text":"warning: Ignoring InnerClasses attribute for an anonymous inner class","sources":[{}]}
AGPBI: {"kind":"error","text":"(org.apache.commons.beanutils.BeanMap$9) that doesn\u0027t come with an","sources":[{}]}
AGPBI: {"kind":"error","text":"associated EnclosingMethod attribute. This class was probably produced by a","sources":[{}]}
AGPBI: {"kind":"error","text":"compiler that did not target the modern .class file format. The recommended","sources":[{}]}
AGPBI: {"kind":"error","text":"solution is to recompile the class from source, using an up-to-date compiler","sources":[{}]}
AGPBI: {"kind":"error","text":"and without specifying any \"-target\" type options. The consequence of ignoring","sources":[{}]}
AGPBI: {"kind":"error","text":"this warning is that reflective operations on this class will incorrectly","sources":[{}]}
AGPBI: {"kind":"error","text":"indicate that it is *not* an inner class.","sources":[{}]}
AGPBI: {"kind":"error","text":"warning: Ignoring InnerClasses attribute for an anonymous inner class","sources":[{}]}
AGPBI: {"kind":"error","text":"(org.apache.commons.beanutils.BeanMap$1) that doesn\u0027t come with an","sources":[{}]}
AGPBI: {"kind":"error","text":"associated EnclosingMethod attribute. This class was probably produced by a","sources":[{}]}
AGPBI: {"kind":"error","text":"compiler that did not target the modern .class file format. The recommended","sources":[{}]}
AGPBI: {"kind":"error","text":"solution is to recompile the class from s

いつから出ていたのかわかりません.

Proguard 周りなのでしょうが, 調べても意味がわかりませんでした.

が,

Large number of errors during Gradle build after upgrading to Android Studio 2.0 - Stack Overflow

意味のない warning や error は, .idea フォルダを消すことで取り除くことができます。このフォルダは Android Studio 1.5 が作成したもので 2.0 にインポートされています。消しても自動的に作成されます。

ideadir

一度削除すると再作成されますが, 意味不明なログが吐かれなくなり, ビルド時間も数分から数十秒に激速となりました。

...

などと喜んでいたが, また出てる.

...

Issue Tacker を見てみる.

Issue 215748 - android - AGPBI reports errors instead of warnings - Android Open Source Project - Issue Tracker - Google Project Hosting

まだ生きてる issue なのですが, 一時しのぎとして以下とか.


# https://code.google.com/p/android/issues/detail?id=215748
-keepattributes Exceptions, Signature, InnerClasses, EnclosingMethod
-dontoptimize

一応出なくなりますけれども,

速くなりますけれども,

「don't optimize」て...

またはこんな一時しのぎ法も.

Issue 222989 - android - Android Studio 2.2 : Build successful with 1944 errors - Android Open Source Project - Issue Tracker - Google Project Hosting

It affects me too it is caused by gradle not android studio (but it tries to update the gradle)
Temporary solution:
I went back to gradle 2.1.3 (that I used earlier):

classpath 'com.android.tools.build:gradle:2.1.3' ->this works

If I am using classpath 'com.android.tools.build:gradle:2.2.0' I am also getting tons of warning message and the output is an unaligned file...

「com.android.tools.build:gradle のバージョンを下げる」という方法ですが, うまくダウングレードできないような...

なんなんすかね, まったく.

issues_-_android_-_android_open_source_project_-_issue_tracker_-_google_project_hosting

Issues - android - Android Open Source Project - Issue Tracker - Google Project Hosting

2016-10-05

待てないので以下でとりあえず対応.
AGPBI 関連の多量のエラーを排除する